<!DOCTYPE html><html lang="zh-CN" data-theme="light"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"><title>JUC并发编程 | 无人深空</title><meta name="keywords" content="JUC"><meta name="author" content="zcm"><meta name="copyright" content="zcm"><meta name="format-detection" content="telephone=no"><meta name="theme-color" content="#ffffff"><meta name="description" content="JUC 并发编程1. Java JUC 简介 JUC 是什么：java 在并发编程中使用的工具包-java.util.concurrent、java.util.concurrent.atomic、java.util.concurrent.locks（均由 Doug Lea 开发） 在 Java 5.0 提供了 java.util.concurrent （简称JUC ）包，在此包中增加了在并发编程中">
<meta property="og:type" content="article">
<meta property="og:title" content="JUC并发编程">
<meta property="og:url" content="https://gitee.com/zcmmmm/zcmmmm/2022/09/24/JUC%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B/index.html">
<meta property="og:site_name" content="无人深空">
<meta property="og:description" content="JUC 并发编程1. Java JUC 简介 JUC 是什么：java 在并发编程中使用的工具包-java.util.concurrent、java.util.concurrent.atomic、java.util.concurrent.locks（均由 Doug Lea 开发） 在 Java 5.0 提供了 java.util.concurrent （简称JUC ）包，在此包中增加了在并发编程中">
<meta property="og:locale" content="zh_CN">
<meta property="og:image" content="https://study-record-images.oss-cn-beijing.aliyuncs.com/Java/JUC/JUC.jpg">
<meta property="article:published_time" content="2022-09-24T02:57:46.000Z">
<meta property="article:modified_time" content="2022-09-27T10:18:32.425Z">
<meta property="article:author" content="zcm">
<meta property="article:tag" content="JUC">
<meta name="twitter:card" content="summary">
<meta name="twitter:image" content="https://study-record-images.oss-cn-beijing.aliyuncs.com/Java/JUC/JUC.jpg"><link rel="shortcut icon" href="/img/favicon.png"><link rel="canonical" href="https://gitee.com/zcmmmm/zcmmmm/2022/09/24/JUC%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B/"><link rel="preconnect" href="//cdn.jsdelivr.net"/><link rel="preconnect" href="//busuanzi.ibruce.info"/><link rel="stylesheet" href="/css/index.css"><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free/css/all.min.css" media="print" onload="this.media='all'"><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/node-snackbar/dist/snackbar.min.css" media="print" onload="this.media='all'"><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fancyapps/ui/dist/fancybox.min.css" media="print" onload="this.media='all'"><script>const GLOBAL_CONFIG = { 
  root: '/',
  algolia: undefined,
  localSearch: {"path":"/search.xml","preload":false,"languages":{"hits_empty":"找不到您查询的内容：${query}"}},
  translate: undefined,
  noticeOutdate: undefined,
  highlight: {"plugin":"highlighjs","highlightCopy":true,"highlightLang":true,"highlightHeightLimit":200},
  copy: {
    success: '复制成功',
    error: '复制错误',
    noSupport: '浏览器不支持'
  },
  relativeDate: {
    homepage: false,
    post: false
  },
  runtime: '天',
  date_suffix: {
    just: '刚刚',
    min: '分钟前',
    hour: '小时前',
    day: '天前',
    month: '个月前'
  },
  copyright: undefined,
  lightbox: 'fancybox',
  Snackbar: {"chs_to_cht":"你已切换为繁体","cht_to_chs":"你已切换为简体","day_to_night":"你已切换为深色模式","night_to_day":"你已切换为浅色模式","bgLight":"#49b1f5","bgDark":"#1f1f1f","position":"bottom-left"},
  source: {
    justifiedGallery: {
      js: 'https://cdn.jsdelivr.net/npm/flickr-justified-gallery/dist/fjGallery.min.js',
      css: 'https://cdn.jsdelivr.net/npm/flickr-justified-gallery/dist/fjGallery.min.css'
    }
  },
  isPhotoFigcaption: false,
  islazyload: false,
  isAnchor: false
}</script><script id="config-diff">var GLOBAL_CONFIG_SITE = {
  title: 'JUC并发编程',
  isPost: true,
  isHome: false,
  isHighlightShrink: false,
  isToc: true,
  postUpdate: '2022-09-27 18:18:32'
}</script><noscript><style type="text/css">
  #nav {
    opacity: 1
  }
  .justified-gallery img {
    opacity: 1
  }

  #recent-posts time,
  #post-meta time {
    display: inline !important
  }
</style></noscript><script>(win=>{
    win.saveToLocal = {
      set: function setWithExpiry(key, value, ttl) {
        if (ttl === 0) return
        const now = new Date()
        const expiryDay = ttl * 86400000
        const item = {
          value: value,
          expiry: now.getTime() + expiryDay,
        }
        localStorage.setItem(key, JSON.stringify(item))
      },

      get: function getWithExpiry(key) {
        const itemStr = localStorage.getItem(key)

        if (!itemStr) {
          return undefined
        }
        const item = JSON.parse(itemStr)
        const now = new Date()

        if (now.getTime() > item.expiry) {
          localStorage.removeItem(key)
          return undefined
        }
        return item.value
      }
    }
  
    win.getScript = url => new Promise((resolve, reject) => {
      const script = document.createElement('script')
      script.src = url
      script.async = true
      script.onerror = reject
      script.onload = script.onreadystatechange = function() {
        const loadState = this.readyState
        if (loadState && loadState !== 'loaded' && loadState !== 'complete') return
        script.onload = script.onreadystatechange = null
        resolve()
      }
      document.head.appendChild(script)
    })
  
      win.activateDarkMode = function () {
        document.documentElement.setAttribute('data-theme', 'dark')
        if (document.querySelector('meta[name="theme-color"]') !== null) {
          document.querySelector('meta[name="theme-color"]').setAttribute('content', '#0d0d0d')
        }
      }
      win.activateLightMode = function () {
        document.documentElement.setAttribute('data-theme', 'light')
        if (document.querySelector('meta[name="theme-color"]') !== null) {
          document.querySelector('meta[name="theme-color"]').setAttribute('content', '#ffffff')
        }
      }
      const t = saveToLocal.get('theme')
    
          if (t === 'dark') activateDarkMode()
          else if (t === 'light') activateLightMode()
        
      const asideStatus = saveToLocal.get('aside-status')
      if (asideStatus !== undefined) {
        if (asideStatus === 'hide') {
          document.documentElement.classList.add('hide-aside')
        } else {
          document.documentElement.classList.remove('hide-aside')
        }
      }
    
    const detectApple = () => {
      if(/iPad|iPhone|iPod|Macintosh/.test(navigator.userAgent)){
        document.documentElement.classList.add('apple')
      }
    }
    detectApple()
    })(window)</script><link rel="stylesheet" href="/css/categories.css?v1"><link rel="stylesheet" href="/css/navigation.css?v1"><link rel="stylesheet" href="/css/custom.css?v1"><meta name="generator" content="Hexo 6.3.0"></head><body><div id="sidebar"><div id="menu-mask"></div><div id="sidebar-menus"><div class="avatar-img is-center"><img src="https://s2.loli.net/2022/09/22/36LN4kcjYOd9HT8.jpg" onerror="onerror=null;src='/img/friend_404.gif'" alt="avatar"/></div><div class="sidebar-site-data site-data is-center"><a href="/archives/"><div class="headline">文章</div><div class="length-num">43</div></a><a href="/tags/"><div class="headline">标签</div><div class="length-num">25</div></a><a href="/categories/"><div class="headline">分类</div><div class="length-num">11</div></a></div><hr/><div class="menus_items"><div class="menus_item"><a class="site-page" href="/"><i class="fa-fw fas fa-home"></i><span> 首页</span></a></div><div class="menus_item"><a class="site-page" href="/archives/"><i class="fa-fw fas fa-archive"></i><span> 时间轴</span></a></div><div class="menus_item"><a class="site-page" href="/tags/"><i class="fa-fw fas fa-tags"></i><span> 标签</span></a></div><div class="menus_item"><a class="site-page" href="/categories/"><i class="fa-fw fas fa-folder-open"></i><span> 分类</span></a></div></div></div></div><div class="post" id="body-wrap"><header class="not-top-img" id="page-header"><nav id="nav"><span id="blog_name"><a id="site-name" href="/">无人深空</a></span><div id="menus"></div><div class="menus_items"><div class="menus_item"><a class="site-page" href="/"><i class="fa-fw fas fa-home"></i><span> 首页</span></a></div><div class="menus_item"><a class="site-page" href="/archives/"><i class="fa-fw fas fa-archive"></i><span> 时间轴</span></a></div><div class="menus_item"><a class="site-page" href="/tags/"><i class="fa-fw fas fa-tags"></i><span> 标签</span></a></div><div class="menus_item"><a class="site-page" href="/categories/"><i class="fa-fw fas fa-folder-open"></i><span> 分类</span></a></div></div><div id="nav-right"><div id="search-button"><a class="site-page social-icon search"><i class="fas fa-search fa-fw"></i></a></div><div id="toggle-menu"><a class="site-page"><i class="fas fa-bars fa-fw"></i></a></div></div></nav></header><main class="layout" id="content-inner"><div id="post"><div id="post-info"><h1 class="post-title">JUC并发编程</h1><div id="post-meta"><div class="meta-firstline"><span class="post-meta-date"><i class="far fa-calendar-alt fa-fw post-meta-icon"></i><span class="post-meta-label">发表于</span><time class="post-meta-date-created" datetime="2022-09-24T02:57:46.000Z" title="发表于 2022-09-24 10:57:46">2022-09-24</time><span class="post-meta-separator">|</span><i class="fas fa-history fa-fw post-meta-icon"></i><span class="post-meta-label">更新于</span><time class="post-meta-date-updated" datetime="2022-09-27T10:18:32.425Z" title="更新于 2022-09-27 18:18:32">2022-09-27</time></span><span class="post-meta-categories"><span class="post-meta-separator">|</span><i class="fas fa-inbox fa-fw post-meta-icon"></i><a class="post-meta-categories" href="/categories/Java/">Java</a></span></div><div class="meta-secondline"><span class="post-meta-separator">|</span><span class="post-meta-wordcount"><i class="far fa-file-word fa-fw post-meta-icon"></i><span class="post-meta-label">字数总计:</span><span class="word-count">19.7k</span><span class="post-meta-separator">|</span><i class="far fa-clock fa-fw post-meta-icon"></i><span class="post-meta-label">阅读时长:</span><span>89分钟</span></span><span class="post-meta-separator">|</span><span class="post-meta-pv-cv" id="" data-flag-title="JUC并发编程"><i class="far fa-eye fa-fw post-meta-icon"></i><span class="post-meta-label">阅读量:</span><span id="busuanzi_value_page_pv"><i class="fa-solid fa-spinner fa-spin"></i></span></span></div></div></div><article class="post-content" id="article-container"><h1 id="JUC-并发编程"><a href="#JUC-并发编程" class="headerlink" title="JUC 并发编程"></a><a target="_blank" rel="noopener" href="https://www.bilibili.com/video/BV1ar4y1x727">JUC 并发编程</a></h1><h2 id="1-Java-JUC-简介"><a href="#1-Java-JUC-简介" class="headerlink" title="1. Java JUC 简介"></a>1. Java JUC 简介</h2><ol>
<li><p>JUC 是什么：java 在并发编程中使用的工具包-<code>java.util.concurrent</code>、<code>java.util.concurrent.atomic</code>、<code>java.util.concurrent.locks</code>（均由 Doug Lea 开发）</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">在 Java 5.0 提供了 java.util.concurrent （简称JUC ）包，在此包中增加了在并发编程中很常用</span><br><span class="line">的实用工具类，用于定义类似于线程的自定义子系统，包括线程池、异步 IO 和轻量级任务框架。</span><br><span class="line">提供可调的、灵活的线程池。还提供了设计用于多线程上下文中的 Collection 实现等</span><br></pre></td></tr></table></figure>
</li>
<li><p>前置知识：IDEA 工具、lombok 插件、Java8 新特性（主要是 lambda 表达式）、JVM 入门（主要是内存模型）</p>
</li>
</ol>
<h2 id="2-线程基础知识复习"><a href="#2-线程基础知识复习" class="headerlink" title="2. 线程基础知识复习"></a>2. 线程基础知识复习</h2><ol>
<li><p>前置知识</p>
<ul>
<li>为什么高并发越来越重要：摩尔定律失效、充分利用 CPU 性能</li>
<li>高并发缺点：线程安全问题、线程锁问题、线程性能问题</li>
</ul>
</li>
<li><p>线程的开始</p>
<ul>
<li>同步方法 <code>Thread.start()</code> 调用 native 方法 <code>start0()</code>，对应 3 个底层源码 <code>thread.c、jvm.cpp、thread.cpp</code></li>
<li><code>native方法</code>调用操作系统层面、由 C&#x2F;C++ 编写的函数</li>
<li>Java &#x3D;&#x3D; (C++)–</li>
</ul>
</li>
<li><p>相关概念</p>
<ul>
<li><p>一个锁：synchronized</p>
</li>
<li><p>两个并：并发（同时段）、并行（同时刻）</p>
</li>
<li><p>三个程：进程、线程、管程（Monitor 即锁）</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">- Monitor 是一种同步机制，保证同一时刻只有一个线程可以访问被保护的数据和代码</span><br><span class="line">- JVM 中同步的实现基于进入和退出 Monitor 实现，每个对象实例都有一个 Monitor 对象</span><br><span class="line">- Monitor 对象和 Java 对象以同创建和销毁，底层由 C++ 实现</span><br></pre></td></tr></table></figure></li>
</ul>
</li>
<li><p>用户线程和守护线程</p>
<ul>
<li>Java 线程通常分为用户线程和守护线程（一般默认用户线程，如<code>main线程</code>，即主线程）</li>
<li>守护线程：为其他线程服务，所有用户线程结束后自动结束。如<code>gc线程</code>，即垃圾回收线程</li>
<li><code>Thread.isDaemon()</code>判断是否为守护线程；<code>Thread.setDaemon()</code>设置守护状态，必须在<code>start()</code>之前设置</li>
</ul>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">    <span class="type">Thread</span> <span class="variable">t1</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Thread</span>(() -&gt; &#123;</span><br><span class="line">        System.out.println(Thread.currentThread().getName() + <span class="string">&quot;\t&quot;</span></span><br><span class="line">                           + (Thread.currentThread().isDaemon() ? <span class="string">&quot;守护线程&quot;</span> : <span class="string">&quot;用户线程&quot;</span>));</span><br><span class="line">        <span class="keyword">while</span> (<span class="literal">true</span>)&#123;&#125;</span><br><span class="line">    &#125;, <span class="string">&quot;t1&quot;</span>);</span><br><span class="line"></span><br><span class="line">    t1.setDaemon(<span class="literal">true</span>);</span><br><span class="line">    t1.start();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">try</span> &#123; TimeUnit.SECONDS.sleep(<span class="number">3</span>); &#125; <span class="keyword">catch</span> (InterruptedException e) &#123; e.printStackTrace(); &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// main线程为用户线程</span></span><br><span class="line">    System.out.println(Thread.currentThread().getName() + <span class="string">&quot;\t&quot;</span></span><br><span class="line">                       + (Thread.currentThread().isDaemon() ? <span class="string">&quot;守护线程&quot;</span> : <span class="string">&quot;用户线程&quot;</span>));</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// output</span></span><br><span class="line">t1	守护线程</span><br><span class="line">main	用户线程</span><br></pre></td></tr></table></figure></li>
</ol>
<h2 id="3-CompetableFuture"><a href="#3-CompetableFuture" class="headerlink" title="3. CompetableFuture"></a>3. CompetableFuture</h2><ol>
<li><p><code>Future</code>接口（jdk1.5）；<code>CompetableFuture</code>接口（jdk1.8）</p>
</li>
<li><p><code>Future</code>接口</p>
<ul>
<li><p>定义了操作<code>异步任务</code>的方法</p>
<p> <img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Java/JUC/Future%E6%8E%A5%E5%8F%A3.png" alt="Future接口"></p>
</li>
<li><p>主要实现类<code>FutureTask</code>、<code>FutureWrapper</code></p>
</li>
</ul>
</li>
<li><p>接口对比（目标：多线程、有返回值、异步任务）</p>
<ul>
<li><code>Runnable</code>接口：无返回值、不抛异常</li>
<li><code>Callable</code>接口：有返回值、抛出异常</li>
<li><code>Future</code>接口：异步任务</li>
</ul>
<p>最终实现 <code>FutureTask</code>：实现 <code>Runnable、Future</code> 接口，并在构造方法<code>public FutureTask(Callable&lt;V&gt; callable)</code>中持有 <code>Callable</code> 接口</p>
<p> <img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Java/JUC/FutureTask%E5%AE%9E%E7%8E%B0.png" alt="FutureTask实现"></p>
<p>基本API演示</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Demo</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> ExecutionException, InterruptedException &#123;</span><br><span class="line">        FutureTask&lt;String&gt; futureTask = <span class="keyword">new</span> <span class="title class_">FutureTask</span>&lt;&gt;(<span class="keyword">new</span> <span class="title class_">MyThread</span>());</span><br><span class="line">        <span class="type">Thread</span> <span class="variable">t1</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Thread</span>(futureTask, <span class="string">&quot;t1&quot;</span>);</span><br><span class="line">        t1.start();</span><br><span class="line"></span><br><span class="line">        System.out.println(futureTask.get());</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">MyThread</span> <span class="keyword">implements</span> <span class="title class_">Callable</span> &#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> Object <span class="title function_">call</span><span class="params">()</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;---- com in call()&quot;</span>);</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;hello Callable&quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// output</span></span><br><span class="line">---- com in <span class="title function_">call</span><span class="params">()</span></span><br><span class="line">hello Callable</span><br></pre></td></tr></table></figure>
</li>
<li><p>Future 接口优缺点</p>
<ul>
<li><p>优点：Future + 线程池异步多线程任务，可以显著提高程序执行效率</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 不用 Future + 线程池</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">    <span class="type">long</span> <span class="variable">startTime</span> <span class="operator">=</span> System.currentTimeMillis();</span><br><span class="line">    <span class="keyword">try</span> &#123; TimeUnit.MILLISECONDS.sleep(<span class="number">500</span>); &#125; <span class="keyword">catch</span> (InterruptedException e) &#123; e.printStackTrace(); &#125;</span><br><span class="line">    <span class="keyword">try</span> &#123; TimeUnit.MILLISECONDS.sleep(<span class="number">200</span>); &#125; <span class="keyword">catch</span> (InterruptedException e) &#123; e.printStackTrace(); &#125;</span><br><span class="line">    <span class="keyword">try</span> &#123; TimeUnit.MILLISECONDS.sleep(<span class="number">300</span>); &#125; <span class="keyword">catch</span> (InterruptedException e) &#123; e.printStackTrace(); &#125;</span><br><span class="line">    <span class="type">long</span> <span class="variable">endTime</span> <span class="operator">=</span> System.currentTimeMillis();</span><br><span class="line">    System.out.println(<span class="string">&quot;constTime: &quot;</span> + (endTime - startTime) + <span class="string">&quot;毫秒&quot;</span>);</span><br><span class="line"></span><br><span class="line">    System.out.println(Thread.currentThread().getName() + <span class="string">&quot;\t&quot;</span> + <span class="string">&quot;end&quot;</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// output</span></span><br><span class="line">constTime: <span class="number">1028</span>毫秒</span><br><span class="line">main	end</span><br></pre></td></tr></table></figure>

<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 使用 Future + 线程池</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> ExecutionException, InterruptedException &#123;</span><br><span class="line">    <span class="type">ExecutorService</span> <span class="variable">threadPool</span> <span class="operator">=</span> Executors.newFixedThreadPool(<span class="number">3</span>);</span><br><span class="line"></span><br><span class="line">    <span class="type">long</span> <span class="variable">startTime</span> <span class="operator">=</span> System.currentTimeMillis();</span><br><span class="line">    FutureTask&lt;String&gt; futureTask1 = <span class="keyword">new</span> <span class="title class_">FutureTask</span>&lt;String&gt;(() -&gt; &#123;</span><br><span class="line">        <span class="keyword">try</span> &#123; TimeUnit.MILLISECONDS.sleep(<span class="number">500</span>); &#125; <span class="keyword">catch</span> (InterruptedException e) &#123; e.printStackTrace(); &#125;</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;futureTask1 over&quot;</span>;</span><br><span class="line">    &#125;);</span><br><span class="line">    threadPool.submit(futureTask1);</span><br><span class="line"></span><br><span class="line">    FutureTask&lt;String&gt; futureTask2 = <span class="keyword">new</span> <span class="title class_">FutureTask</span>&lt;String&gt;(() -&gt; &#123;</span><br><span class="line">        <span class="keyword">try</span> &#123; TimeUnit.MILLISECONDS.sleep(<span class="number">200</span>); &#125; <span class="keyword">catch</span> (InterruptedException e) &#123; e.printStackTrace(); &#125;</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;futureTask2 over&quot;</span>;</span><br><span class="line">    &#125;);</span><br><span class="line">    threadPool.submit(futureTask2);</span><br><span class="line"></span><br><span class="line">    FutureTask&lt;String&gt; futureTask3 = <span class="keyword">new</span> <span class="title class_">FutureTask</span>&lt;String&gt;(() -&gt; &#123;</span><br><span class="line">        <span class="keyword">try</span> &#123; TimeUnit.MILLISECONDS.sleep(<span class="number">300</span>); &#125; <span class="keyword">catch</span> (InterruptedException e) &#123; e.printStackTrace(); &#125;</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;futureTask3 over&quot;</span>;</span><br><span class="line">    &#125;);</span><br><span class="line">    threadPool.submit(futureTask3);</span><br><span class="line"></span><br><span class="line">    futureTask1.get();</span><br><span class="line">    futureTask2.get();</span><br><span class="line">    futureTask3.get();</span><br><span class="line"></span><br><span class="line">    <span class="type">long</span> <span class="variable">endTime</span> <span class="operator">=</span> System.currentTimeMillis();</span><br><span class="line">    System.out.println(<span class="string">&quot;constTime: &quot;</span> + (endTime - startTime) + <span class="string">&quot;毫秒&quot;</span>);</span><br><span class="line"></span><br><span class="line">    System.out.println(Thread.currentThread().getName() + <span class="string">&quot;\t&quot;</span> + <span class="string">&quot;end&quot;</span>);</span><br><span class="line">    threadPool.shutdown();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// output</span></span><br><span class="line">constTime: <span class="number">633</span>毫秒</span><br><span class="line">main	end</span><br></pre></td></tr></table></figure>
</li>
<li><p>缺点：</p>
<ul>
<li><p><code>get()</code>方法阻塞，调用 <code>FutureTask.get()</code>后直到该线程完成任务才继续执行 main 线程</p>
<p>所以为了实现<code>异步</code>效果，<code>get()</code>方法一般最后调用（治标不治本）</p>
<p>或者设置超时时间<code>public V get(long timeout, TimeUnit unit)</code>，超过指定时间仍没有取得返回值则抛出异常</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> ExecutionException, InterruptedException &#123;</span><br><span class="line">    FutureTask&lt;String&gt; futureTask1 = <span class="keyword">new</span> <span class="title class_">FutureTask</span>&lt;String&gt;(() -&gt; &#123;</span><br><span class="line">        System.out.println(Thread.currentThread().getName() + <span class="string">&quot;\t&quot;</span> + <span class="string">&quot;come in&quot;</span>);</span><br><span class="line">        <span class="keyword">try</span> &#123; TimeUnit.SECONDS.sleep(<span class="number">3</span>); &#125; <span class="keyword">catch</span> (InterruptedException e) &#123; e.printStackTrace(); &#125;</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;futureTask1 over&quot;</span>;</span><br><span class="line">    &#125;);</span><br><span class="line">    <span class="keyword">new</span> <span class="title class_">Thread</span>(futureTask1).start();</span><br><span class="line">    <span class="comment">// 不调用 futureTask1.get()，马上执行后序代码</span></span><br><span class="line">    <span class="comment">// 调用 futureTask1.get()，阻塞直到 futureTask1 执行完成获得返回值</span></span><br><span class="line">    System.out.println(futureTask1.get());</span><br><span class="line">    <span class="comment">// 设置过期时间</span></span><br><span class="line">    <span class="comment">// System.out.println(futureTask1.get(5, TimeUnit.SECONDS));</span></span><br><span class="line"></span><br><span class="line">    System.out.println(Thread.currentThread().getName() + <span class="string">&quot;\t&quot;</span> + <span class="string">&quot;继续完成其他任务&quot;</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// output</span></span><br><span class="line">Thread-<span class="number">0</span>	come in</span><br><span class="line">futureTask1 over</span><br><span class="line">main	继续完成其他任务</span><br></pre></td></tr></table></figure>
</li>
<li><p><code>isDone()</code> 轮询，容易导致 CPU 空转，浪费资源</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> ExecutionException, InterruptedException &#123;</span><br><span class="line">    FutureTask&lt;String&gt; futureTask1 = <span class="keyword">new</span> <span class="title class_">FutureTask</span>&lt;String&gt;(() -&gt; &#123;</span><br><span class="line">        System.out.println(Thread.currentThread().getName() + <span class="string">&quot;\t&quot;</span> + <span class="string">&quot;come in&quot;</span>);</span><br><span class="line">        <span class="keyword">try</span> &#123; TimeUnit.SECONDS.sleep(<span class="number">3</span>); &#125; <span class="keyword">catch</span> (InterruptedException e) &#123; e.printStackTrace(); &#125;</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;futureTask1 over&quot;</span>;</span><br><span class="line">    &#125;);</span><br><span class="line">    <span class="keyword">new</span> <span class="title class_">Thread</span>(futureTask1).start();</span><br><span class="line">    <span class="keyword">while</span> (<span class="literal">true</span>) &#123;</span><br><span class="line">        <span class="keyword">if</span>(futureTask1.isDone()) &#123;</span><br><span class="line">            System.out.println(futureTask1.get());</span><br><span class="line">            <span class="keyword">break</span>;</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="keyword">try</span> &#123; TimeUnit.SECONDS.sleep(<span class="number">1</span>); &#125; <span class="keyword">catch</span> (InterruptedException e) &#123; e.printStackTrace(); &#125;</span><br><span class="line">            System.out.println(<span class="string">&quot;每隔一段时间后查询 futureTask 是否完成&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    System.out.println(Thread.currentThread().getName() + <span class="string">&quot;\t&quot;</span> + <span class="string">&quot;继续完成其他任务&quot;</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// output</span></span><br><span class="line">Thread-<span class="number">0</span>	come in</span><br><span class="line">每隔一段时间后查询 futureTask 是否完成</span><br><span class="line">每隔一段时间后查询 futureTask 是否完成</span><br><span class="line">每隔一段时间后查询 futureTask 是否完成</span><br><span class="line">futureTask1 over</span><br><span class="line">main	继续完成其他任务</span><br></pre></td></tr></table></figure></li>
</ul>
</li>
</ul>
</li>
<li><p>CompletableFuture 优化 Future</p>
<ul>
<li><p>Future 对于结果获取不友好，只能阻塞或者轮询</p>
</li>
<li><p>优化思路：完成后自动通知（回调通知）</p>
</li>
<li><p>CompletableFuture 可实现更复杂需求：多个有依赖关系的异步任务调度</p>
</li>
<li><p>CompletionStage 接口：代表一部计算过程中的一个阶段，一个阶段完成后可能触发另一个阶段</p>
<p> <img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Java/JUC/CompletionFuture%E5%AE%9E%E7%8E%B0.png" alt="CompletionFuture实现"></p>
</li>
<li><p>CompletableFuture 有构造方法 <code>public CompletableFuture()</code> ，但是不推荐使用，尽量使用四个静态方法获取实例</p>
</li>
</ul>
</li>
<li><p>CompletableFuture 四个静态方法</p>
<ul>
<li><p>runAsync：无返回值</p>
<ul>
<li><code>public static CompletableFuture&lt;Void&gt; runAsync(Runnable runnable)</code></li>
<li><code>public static CompletableFuture&lt;Void&gt; runAsync(Runnable runnable, Executor exexutor)</code></li>
</ul>
</li>
<li><p>supplyAsync：有返回值（常用）</p>
<ul>
<li><code>public static &lt;U&gt; CompletableFuture&lt;U&gt; supplyAsync(Runnable runnable)</code></li>
<li><code>public static &lt;U&gt; CompletableFuture&lt;U&gt; supplyAsync(Runnable runnable, Executor exexutor)</code></li>
</ul>
</li>
<li><p>不指定线程池（Executor），则使用默认的 <code>ForkJoinPool.commonPool()</code> 作为线程池</p>
<p>注意默认线程池会在主线程结束后自动关闭，详情看代码及注释</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> ExecutionException, InterruptedException &#123;</span><br><span class="line">    CompletableFuture.supplyAsync(() -&gt; &#123;</span><br><span class="line">        System.out.println(Thread.currentThread().getName() + <span class="string">&quot;\t come in&quot;</span>);</span><br><span class="line">        <span class="type">int</span> <span class="variable">result</span> <span class="operator">=</span> ThreadLocalRandom.current().nextInt(<span class="number">10</span>);</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            TimeUnit.SECONDS.sleep(<span class="number">1</span>);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (InterruptedException e) &#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">        &#125;</span><br><span class="line">        System.out.println(<span class="string">&quot;1s后结果为: &quot;</span> + result);</span><br><span class="line">        <span class="keyword">return</span> result;</span><br><span class="line">    &#125;).whenComplete((v, e) -&gt; &#123;</span><br><span class="line">        <span class="keyword">if</span>(e == <span class="literal">null</span>) &#123;</span><br><span class="line">            System.out.println(<span class="string">&quot;计算完成， result = &quot;</span> + v);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;).exceptionally(e -&gt; &#123;</span><br><span class="line">        e.printStackTrace();</span><br><span class="line">        System.out.println(<span class="string">&quot;发生异常: &quot;</span> + e.getCause() + <span class="string">&quot;\t&quot;</span> + e.getMessage());</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">    &#125;);</span><br><span class="line"></span><br><span class="line">    System.out.println(Thread.currentThread().getName() + <span class="string">&quot;\t 继续执行任务&quot;</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 主线程暂停3秒后结束。因为默认线程池在主线程完成后会立刻关闭</span></span><br><span class="line">    <span class="comment">// 若不暂停则不打印后结果两行</span></span><br><span class="line">    <span class="keyword">try</span> &#123; TimeUnit.SECONDS.sleep(<span class="number">3</span>); &#125; <span class="keyword">catch</span> (InterruptedException e) &#123; e.printStackTrace(); &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// output</span></span><br><span class="line">ForkJoinPool.commonPool-worker-<span class="number">1</span>	 come in</span><br><span class="line">main	 继续执行任务</span><br><span class="line">1s后结果为: <span class="number">6</span></span><br><span class="line">计算完成， result = <span class="number">6</span></span><br></pre></td></tr></table></figure>
</li>
<li><p>自定义线程池（常用）</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> ExecutionException, InterruptedException &#123;</span><br><span class="line">    <span class="type">ExecutorService</span> <span class="variable">threadPool</span> <span class="operator">=</span> Executors.newFixedThreadPool(<span class="number">3</span>);</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        CompletableFuture.supplyAsync(() -&gt; &#123;</span><br><span class="line">            System.out.println(Thread.currentThread().getName() + <span class="string">&quot;\t come in&quot;</span>);</span><br><span class="line">            <span class="type">int</span> <span class="variable">result</span> <span class="operator">=</span> ThreadLocalRandom.current().nextInt(<span class="number">10</span>);</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                TimeUnit.SECONDS.sleep(<span class="number">1</span>);</span><br><span class="line">            &#125; <span class="keyword">catch</span> (InterruptedException e) &#123;</span><br><span class="line">                e.printStackTrace();</span><br><span class="line">            &#125;</span><br><span class="line">            System.out.println(<span class="string">&quot;1s后结果为: &quot;</span> + result);</span><br><span class="line">            <span class="keyword">return</span> result;</span><br><span class="line">        &#125;, threadPool).whenComplete((v, e) -&gt; &#123;</span><br><span class="line">            <span class="keyword">if</span>(e == <span class="literal">null</span>) &#123;</span><br><span class="line">                System.out.println(<span class="string">&quot;计算完成， result = &quot;</span> + v);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;).exceptionally(e -&gt; &#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">            System.out.println(<span class="string">&quot;发生异常: &quot;</span> + e.getCause() + <span class="string">&quot;\t&quot;</span> + e.getMessage());</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">        &#125;);</span><br><span class="line"></span><br><span class="line">        System.out.println(Thread.currentThread().getName() + <span class="string">&quot;\t 继续执行任务&quot;</span>);</span><br><span class="line">    &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">        threadPool.shutdown();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// output</span></span><br><span class="line">pool-<span class="number">1</span>-thread-<span class="number">1</span>	 come in</span><br><span class="line">main	 继续执行任务</span><br><span class="line">1s后结果为: <span class="number">7</span></span><br><span class="line">计算完成， result = <span class="number">7</span></span><br></pre></td></tr></table></figure></li>
</ul>
</li>
<li><p>CompletableFuture 优点</p>
<ul>
<li>异步任务完成后自动执行回调方法</li>
<li>异步任务间可以按指定顺序执行</li>
<li>异步任务出错也可以通过<code>exceptionally()</code>设置回调方法</li>
</ul>
</li>
<li><p>函数式接口（JDK1.8 后函数式编程逐渐成为主流）</p>
<p><img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Java/JUC/%E5%87%BD%E6%95%B0%E5%BC%8F%E6%8E%A5%E5%8F%A3.png" alt="函数式接口"></p>
</li>
<li><p>CompletableFuture 链式调用（Chain）、流式调用（Stream）</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Demo</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> ExecutionException, InterruptedException &#123;</span><br><span class="line">        <span class="type">Student</span> <span class="variable">student</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Student</span>();</span><br><span class="line">        <span class="comment">// 普通调用</span></span><br><span class="line">        student.setId(<span class="number">1</span>);</span><br><span class="line">        student.setStudentName(<span class="string">&quot;zcm&quot;</span>);</span><br><span class="line">        student.setMajor(<span class="string">&quot;SE&quot;</span>);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 链式调用，需添加lombok注解: @Accessors(chain = true)</span></span><br><span class="line">        student.setId(<span class="number">1</span>).setStudentName(<span class="string">&quot;zcm&quot;</span>).setMajor(<span class="string">&quot;SE&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">@AllArgsConstructor</span></span><br><span class="line"><span class="meta">@NoArgsConstructor</span></span><br><span class="line"><span class="meta">@Data</span></span><br><span class="line"><span class="meta">@Accessors(chain = true)</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Student</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> Integer id;</span><br><span class="line">    <span class="keyword">private</span> String studentName;</span><br><span class="line">    <span class="keyword">private</span> String major;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
</li>
<li><p>CompletableFuture 中 <code>get()</code>和<code>join()</code>方法的区别（都能获取返回值）</p>
<ul>
<li>get()：需要抛出异常</li>
<li>join()：无需抛出异常</li>
</ul>
</li>
<li><p>电商比价 + 流式计算</p>
<ul>
<li><p>初级版</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Demo</span> &#123;</span><br><span class="line">    <span class="keyword">static</span> List&lt;NetMall&gt; list = Arrays.asList(</span><br><span class="line">            <span class="keyword">new</span> <span class="title class_">NetMall</span>(<span class="string">&quot;jingdong&quot;</span>),</span><br><span class="line">            <span class="keyword">new</span> <span class="title class_">NetMall</span>(<span class="string">&quot;dangdang&quot;</span>),</span><br><span class="line">            <span class="keyword">new</span> <span class="title class_">NetMall</span>(<span class="string">&quot;taobao&quot;</span>)</span><br><span class="line">    );</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> List&lt;String&gt; <span class="title function_">getPrice</span><span class="params">(List&lt;NetMall&gt; list, String productName)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> list</span><br><span class="line">                .stream()</span><br><span class="line">                .map(netMall -&gt;</span><br><span class="line">                        String.format(productName + <span class="string">&quot; in %s price is %.2f&quot;</span>,</span><br><span class="line">                                netMall.getNetMallName(),</span><br><span class="line">                                netMall.calcPrice(productName)))</span><br><span class="line">                .collect(Collectors.toList());</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> ExecutionException, InterruptedException &#123;</span><br><span class="line">        <span class="type">long</span> <span class="variable">startTime</span> <span class="operator">=</span> System.currentTimeMillis();</span><br><span class="line">        List&lt;String&gt; list1 = getPrice(list, <span class="string">&quot;mysql&quot;</span>);</span><br><span class="line">        <span class="keyword">for</span> (String element : list1) &#123;</span><br><span class="line">            System.out.println(element);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="type">long</span> <span class="variable">endTime</span> <span class="operator">=</span> System.currentTimeMillis();</span><br><span class="line">        System.out.println(<span class="string">&quot;constTime: &quot;</span> + (endTime - startTime) + <span class="string">&quot;毫秒&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Data</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">NetMall</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> String netMallName;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">NetMall</span><span class="params">(String netMallName)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.netMallName = netMallName;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="type">double</span> <span class="title function_">calcPrice</span><span class="params">(String productName)</span> &#123;</span><br><span class="line">        <span class="keyword">try</span> &#123; TimeUnit.SECONDS.sleep(<span class="number">1</span>); &#125; <span class="keyword">catch</span> (InterruptedException e) &#123; e.printStackTrace(); &#125;</span><br><span class="line">        <span class="keyword">return</span> ThreadLocalRandom.current().nextDouble() * <span class="number">2</span> + productName.charAt(<span class="number">0</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// output</span></span><br><span class="line">mysql in jingdong price is <span class="number">109.59</span></span><br><span class="line">mysql in dangdang price is <span class="number">110.04</span></span><br><span class="line">mysql in taobao price is <span class="number">109.52</span></span><br><span class="line">constTime: <span class="number">3164</span>毫秒</span><br></pre></td></tr></table></figure>
</li>
<li><p>高级版（相比较初级版，将 <code>getPrice</code> 函数改为 <code>getPriceByCompletableFuture</code>）</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> List&lt;String&gt; <span class="title function_">getPriceByCompletableFuture</span><span class="params">(List&lt;NetMall&gt; list, String productName)</span> &#123;</span><br><span class="line">    <span class="keyword">return</span> list.stream()</span><br><span class="line">        .map(netMall -&gt;</span><br><span class="line">             CompletableFuture.supplyAsync(() -&gt; String.format(productName + <span class="string">&quot; in %s price is %.2f&quot;</span>,</span><br><span class="line">                                                               netMall.getNetMallName(),</span><br><span class="line">                                                               netMall.calcPrice(productName))))</span><br><span class="line">        .collect(Collectors.toList())</span><br><span class="line">        .stream()</span><br><span class="line">        .map(s -&gt; s.join())</span><br><span class="line">        .collect(Collectors.toList());</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> ExecutionException, InterruptedException &#123;</span><br><span class="line">    <span class="type">long</span> <span class="variable">startTime</span> <span class="operator">=</span> System.currentTimeMillis();</span><br><span class="line">    List&lt;String&gt; list1 = getPriceByCompletableFuture(list, <span class="string">&quot;mysql&quot;</span>);</span><br><span class="line">    <span class="keyword">for</span> (String element : list1) &#123;</span><br><span class="line">        System.out.println(element);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="type">long</span> <span class="variable">endTime</span> <span class="operator">=</span> System.currentTimeMillis();</span><br><span class="line">    System.out.println(<span class="string">&quot;constTime: &quot;</span> + (endTime - startTime) + <span class="string">&quot;毫秒&quot;</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// output</span></span><br><span class="line">mysql in jingdong price is <span class="number">110.64</span></span><br><span class="line">mysql in dangdang price is <span class="number">110.28</span></span><br><span class="line">mysql in taobao price is <span class="number">110.18</span></span><br><span class="line">constTime: <span class="number">1077</span>毫秒</span><br></pre></td></tr></table></figure></li>
</ul>
</li>
<li><p>CompletableFuture 常用方法（分类介绍）</p>
<ul>
<li><p>获得结果和触发计算：<code>get()、get(long timeout, TimeUnit unit)、join、getNow(T valueIfAbsent)、complete()</code></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> ExecutionException, InterruptedException, TimeoutException &#123;</span><br><span class="line">    <span class="type">CompletableFuture</span> <span class="variable">completableFuture</span> <span class="operator">=</span> CompletableFuture.supplyAsync(() -&gt; &#123;</span><br><span class="line">        <span class="keyword">try</span> &#123; TimeUnit.SECONDS.sleep(<span class="number">1</span>); &#125; <span class="keyword">catch</span> (InterruptedException e) &#123; e.printStackTrace(); &#125;</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;test&quot;</span>;</span><br><span class="line">    &#125;);</span><br><span class="line">    <span class="comment">// 1.get() 会阻塞，需要抛异常</span></span><br><span class="line">    <span class="comment">//        System.out.println(completableFuture.get());</span></span><br><span class="line">    <span class="comment">// 2.get(long timeout, TimeUnit unit) 超出限制时间则抛异常</span></span><br><span class="line">    <span class="comment">//        System.out.println(completableFuture.get(2L, TimeUnit.SECONDS));</span></span><br><span class="line">    <span class="comment">// 3.join() 会阻塞，不抛异常</span></span><br><span class="line">    <span class="comment">//        System.out.println(completableFuture.join());</span></span><br><span class="line">    <span class="comment">// 4.getNow(T valueIfAbsent) 如计算未完成则返回默认值: valueIfAbsent</span></span><br><span class="line">    <span class="comment">//        System.out.println(completableFuture.getNow(&quot;计算未完成&quot;));</span></span><br><span class="line">    <span class="comment">// 5.complete(T value) 效果类似 getNow</span></span><br><span class="line">    System.out.println(completableFuture.complete(<span class="string">&quot;计算未完成&quot;</span>) + <span class="string">&quot;\t&quot;</span> + completableFuture.join());</span><br><span class="line"></span><br><span class="line">    System.out.println(Thread.currentThread().getName() + <span class="string">&quot;\t 继续执行&quot;</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
</li>
<li><p>对计算结果进行处理：<code>thenApply()、handle()</code>，计算结果存在依赖，将线程串行化</p>
<ul>
<li><p><code>thenApply()</code>当前步骤有异常则叫停，不再继续执行（常用）</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> ExecutionException, InterruptedException, TimeoutException &#123;</span><br><span class="line">    <span class="type">ExecutorService</span> <span class="variable">threadPool</span> <span class="operator">=</span> Executors.newFixedThreadPool(<span class="number">3</span>);</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        <span class="type">CompletableFuture</span> <span class="variable">completableFuture</span> <span class="operator">=</span> CompletableFuture.supplyAsync(() -&gt; &#123;</span><br><span class="line">            <span class="keyword">try</span> &#123; TimeUnit.SECONDS.sleep(<span class="number">1</span>); &#125; <span class="keyword">catch</span> (InterruptedException e) &#123; e.printStackTrace(); &#125;</span><br><span class="line">            System.out.println(<span class="string">&quot;111&quot;</span>);</span><br><span class="line">            <span class="keyword">return</span> <span class="number">1</span>;</span><br><span class="line">        &#125;, threadPool).thenApply(f -&gt; &#123;</span><br><span class="line">            <span class="type">int</span> <span class="variable">num</span> <span class="operator">=</span> <span class="number">10</span> / <span class="number">0</span>;</span><br><span class="line">            System.out.println(<span class="string">&quot;222&quot;</span>);</span><br><span class="line">            <span class="keyword">return</span> f + <span class="number">2</span>;</span><br><span class="line">        &#125;).thenApply(f -&gt; &#123;</span><br><span class="line">            System.out.println(<span class="string">&quot;333&quot;</span>);</span><br><span class="line">            <span class="keyword">return</span> f + <span class="number">1</span>;</span><br><span class="line">        &#125;).whenComplete((v, e) -&gt; &#123;</span><br><span class="line">            <span class="keyword">if</span>(e == <span class="literal">null</span>) &#123;</span><br><span class="line">                System.out.println(<span class="string">&quot;计算结果: &quot;</span> + v);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;).exceptionally(e -&gt; &#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">            System.out.println(e.getMessage());</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">        &#125;);</span><br><span class="line">    &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">        threadPool.shutdown();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    System.out.println(Thread.currentThread().getName() + <span class="string">&quot;\t 继续执行&quot;</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// output</span></span><br><span class="line">main	 继续执行</span><br><span class="line"><span class="number">111</span></span><br><span class="line">java.lang.ArithmeticException: / by zero</span><br></pre></td></tr></table></figure>
</li>
<li><p><code>handle()</code>当前步骤有异常则跳过，继续执行</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> ExecutionException, InterruptedException, TimeoutException &#123;</span><br><span class="line">    <span class="type">ExecutorService</span> <span class="variable">threadPool</span> <span class="operator">=</span> Executors.newFixedThreadPool(<span class="number">3</span>);</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        <span class="type">CompletableFuture</span> <span class="variable">completableFuture</span> <span class="operator">=</span> CompletableFuture.supplyAsync(() -&gt; &#123;</span><br><span class="line">            <span class="keyword">try</span> &#123; TimeUnit.SECONDS.sleep(<span class="number">1</span>); &#125; <span class="keyword">catch</span> (InterruptedException e) &#123; e.printStackTrace(); &#125;</span><br><span class="line">            System.out.println(<span class="string">&quot;111&quot;</span>);</span><br><span class="line">            <span class="keyword">return</span> <span class="number">1</span>;</span><br><span class="line">        &#125;, threadPool).handle((f, e) -&gt; &#123;</span><br><span class="line">            <span class="type">int</span> <span class="variable">num</span> <span class="operator">=</span> <span class="number">10</span> / <span class="number">0</span>;</span><br><span class="line">            System.out.println(<span class="string">&quot;222&quot;</span>);</span><br><span class="line">            <span class="keyword">return</span> f + <span class="number">2</span>;</span><br><span class="line">        &#125;).handle((f, e) -&gt; &#123;</span><br><span class="line">            System.out.println(<span class="string">&quot;333&quot;</span>);</span><br><span class="line">            <span class="keyword">return</span> f + <span class="number">1</span>;</span><br><span class="line">        &#125;).whenComplete((v, e) -&gt; &#123;</span><br><span class="line">            <span class="keyword">if</span>(e == <span class="literal">null</span>) &#123;</span><br><span class="line">                System.out.println(<span class="string">&quot;计算结果: &quot;</span> + v);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;).exceptionally(e -&gt; &#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">            System.out.println(e.getMessage());</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">        &#125;);</span><br><span class="line">    &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">        threadPool.shutdown();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    System.out.println(Thread.currentThread().getName() + <span class="string">&quot;\t 继续执行&quot;</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// output</span></span><br><span class="line">main	 继续执行</span><br><span class="line"><span class="number">111</span></span><br><span class="line"><span class="number">333</span></span><br><span class="line">java.lang.NullPointerException</span><br></pre></td></tr></table></figure></li>
</ul>
</li>
<li><p>对计算结果进行消费：接收并消费任务的处理结果，无返回值。<code>thenAccept()</code></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> ExecutionException, InterruptedException, TimeoutException &#123;</span><br><span class="line">    <span class="type">ExecutorService</span> <span class="variable">threadPool</span> <span class="operator">=</span> Executors.newFixedThreadPool(<span class="number">3</span>);</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        <span class="type">CompletableFuture</span> <span class="variable">completableFuture</span> <span class="operator">=</span> CompletableFuture.supplyAsync(() -&gt; &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="number">1</span>;</span><br><span class="line">        &#125;, threadPool).thenAccept(f -&gt; &#123;</span><br><span class="line">            System.out.println(<span class="string">&quot;返回值: &quot;</span> + f);</span><br><span class="line">        &#125;);</span><br><span class="line">    &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">        threadPool.shutdown();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    System.out.println(Thread.currentThread().getName() + <span class="string">&quot;\t 继续执行&quot;</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// output</span></span><br><span class="line">main	 继续执行</span><br><span class="line">返回值: <span class="number">1</span></span><br></pre></td></tr></table></figure>

<ul>
<li><p><code>thenRun()、thenApply()、thenAccept()</code>对比</p>
<ul>
<li><code>thenRun(Runnable runnable)</code>：任务 A 执行完后执行任务 B，B 不需要 A 的结果，无返回值（ runnable 的 run 方法无返回值）</li>
<li><code>thenApply(Function fn)</code>：任务 A 执行完后执行任务 B，B 需要 A 的结果，且 B 有返回值</li>
<li><code>thenAccept(Consumer action)</code>：任务 A 执行完后执行任务 B，B 需要 A 的结果，但 B 无返回值</li>
</ul>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">    System.out.println(<span class="string">&quot;thenRun 无返回值\t&quot;</span> + CompletableFuture.supplyAsync(() -&gt; <span class="string">&quot;resultA&quot;</span>).thenRun(() -&gt; &#123;&#125;).join());</span><br><span class="line">    System.out.println(<span class="string">&quot;thenApply 有返回值\t&quot;</span> + CompletableFuture.supplyAsync(() -&gt; <span class="string">&quot;resultA&quot;</span>).thenApply(r -&gt; r + <span class="string">&quot; resultB&quot;</span>).join());</span><br><span class="line">    System.out.println(<span class="string">&quot;thenAccept 无返回值\t&quot;</span> + CompletableFuture.supplyAsync(() -&gt; <span class="string">&quot;resultA&quot;</span>).thenAccept(r -&gt; System.out.println(<span class="string">&quot;thenAccept &quot;</span> + r)).join());</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// output</span></span><br><span class="line">thenRun 无返回值	<span class="literal">null</span></span><br><span class="line">thenApply 有返回值	resultA resultB</span><br><span class="line">thenAccept resultA</span><br><span class="line">thenAccept 无返回值	<span class="literal">null</span></span><br></pre></td></tr></table></figure></li>
</ul>
</li>
<li><p>对计算速度进行选用：谁快选谁<code>applyToEither()</code>，谁调用无所谓</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span>  &#123;</span><br><span class="line">    CompletableFuture&lt;String&gt; playA = CompletableFuture.supplyAsync(() -&gt; &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;A come in&quot;</span>);</span><br><span class="line">        <span class="keyword">try</span> &#123; TimeUnit.SECONDS.sleep(<span class="number">2</span>); &#125; <span class="keyword">catch</span> (InterruptedException e) &#123; e.printStackTrace(); &#125;</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;playA&quot;</span>;</span><br><span class="line">    &#125;);</span><br><span class="line"></span><br><span class="line">    CompletableFuture&lt;String&gt; playB = CompletableFuture.supplyAsync(() -&gt; &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;B come in&quot;</span>);</span><br><span class="line">        <span class="keyword">try</span> &#123; TimeUnit.SECONDS.sleep(<span class="number">1</span>); &#125; <span class="keyword">catch</span> (InterruptedException e) &#123; e.printStackTrace(); &#125;</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;playB&quot;</span>;</span><br><span class="line">    &#125;);</span><br><span class="line">    </span><br><span class="line">	<span class="comment">// 谁调用 applyToEither 无所谓，结果相同</span></span><br><span class="line">    CompletableFuture&lt;String&gt; result1 = playA.applyToEither(playB, f -&gt; &#123;</span><br><span class="line">        <span class="keyword">return</span> f + <span class="string">&quot; is winer in result1&quot;</span>;</span><br><span class="line">    &#125;);</span><br><span class="line">    </span><br><span class="line">    CompletableFuture&lt;String&gt; result2 = playB.applyToEither(playA, f -&gt; &#123;</span><br><span class="line">        <span class="keyword">return</span> f + <span class="string">&quot; is winer in result2&quot;</span>;</span><br><span class="line">    &#125;);</span><br><span class="line"></span><br><span class="line">    System.out.println(Thread.currentThread().getName() + <span class="string">&quot;\t&quot;</span> + result1.join());</span><br><span class="line">        System.out.println(Thread.currentThread().getName() + <span class="string">&quot;\t&quot;</span> + result2.join());</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// output</span></span><br><span class="line">A come in</span><br><span class="line">B come in</span><br><span class="line">main	playB is winer in result1</span><br><span class="line">main	playB is winer in result2</span><br></pre></td></tr></table></figure>
</li>
<li><p>对计算结果进行合并：<code>thenCombine()</code>，先完成的等着，等其他都完成后合并</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span>  &#123;</span><br><span class="line">    CompletableFuture&lt;Integer&gt; task1 = CompletableFuture.supplyAsync(() -&gt; &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;1 come in&quot;</span>);</span><br><span class="line">        <span class="keyword">try</span> &#123; TimeUnit.SECONDS.sleep(<span class="number">2</span>); &#125; <span class="keyword">catch</span> (InterruptedException e) &#123; e.printStackTrace(); &#125;</span><br><span class="line">        <span class="keyword">return</span> <span class="number">10</span>;</span><br><span class="line">    &#125;);</span><br><span class="line"></span><br><span class="line">    CompletableFuture&lt;Integer&gt; task2 = CompletableFuture.supplyAsync(() -&gt; &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;2 come in&quot;</span>);</span><br><span class="line">        <span class="keyword">try</span> &#123; TimeUnit.SECONDS.sleep(<span class="number">1</span>); &#125; <span class="keyword">catch</span> (InterruptedException e) &#123; e.printStackTrace(); &#125;</span><br><span class="line">        <span class="keyword">return</span> <span class="number">20</span>;</span><br><span class="line">    &#125;);</span><br><span class="line"></span><br><span class="line">    CompletableFuture&lt;Integer&gt; result = task1.thenCombine(task2, (x, y) -&gt; &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;结果集合并&quot;</span>);</span><br><span class="line">        <span class="keyword">return</span> x + y;</span><br><span class="line">    &#125;);</span><br><span class="line"></span><br><span class="line">    System.out.println(Thread.currentThread().getName() + <span class="string">&quot;\t&quot;</span> + result.join());</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// output</span></span><br><span class="line"><span class="number">1</span> come in</span><br><span class="line"><span class="number">2</span> come in</span><br><span class="line">结果集合并</span><br><span class="line">main	<span class="number">30</span></span><br></pre></td></tr></table></figure></li>
</ul>
</li>
<li><p>线程池线程的选择</p>
<ul>
<li>使用默认线程池：全部使用默认线程池</li>
<li>使用自定义线程池：<ul>
<li><code>thenRun()</code>与上一步用相同线程池，即跟随</li>
<li><code>thenRunAsync()</code>用默认线程池，即另起炉灶</li>
</ul>
</li>
</ul>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> ExecutionException, InterruptedException, TimeoutException &#123;</span><br><span class="line">    <span class="type">ExecutorService</span> <span class="variable">threadPool</span> <span class="operator">=</span> Executors.newFixedThreadPool(<span class="number">5</span>);</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        CompletableFuture&lt;Void&gt; completableFuture = CompletableFuture.supplyAsync(() -&gt; &#123;</span><br><span class="line">            <span class="keyword">try</span> &#123; TimeUnit.MILLISECONDS.sleep(<span class="number">20</span>); &#125; <span class="keyword">catch</span> (InterruptedException e) &#123; e.printStackTrace(); &#125;</span><br><span class="line">            System.out.println(<span class="string">&quot;1号任务\t&quot;</span> + Thread.currentThread().getName());</span><br><span class="line">            <span class="keyword">return</span> <span class="string">&quot;abcd&quot;</span>;</span><br><span class="line">        &#125;, threadPool).thenRun(() -&gt; &#123;</span><br><span class="line">            <span class="keyword">try</span> &#123; TimeUnit.MILLISECONDS.sleep(<span class="number">20</span>); &#125; <span class="keyword">catch</span> (InterruptedException e) &#123; e.printStackTrace(); &#125;</span><br><span class="line">            System.out.println(<span class="string">&quot;2号任务\t&quot;</span> + Thread.currentThread().getName());</span><br><span class="line">        &#125;).thenRunAsync(() -&gt; &#123;</span><br><span class="line">            <span class="keyword">try</span> &#123; TimeUnit.MILLISECONDS.sleep(<span class="number">20</span>); &#125; <span class="keyword">catch</span> (InterruptedException e) &#123; e.printStackTrace(); &#125;</span><br><span class="line">            System.out.println(<span class="string">&quot;3号任务\t&quot;</span> + Thread.currentThread().getName());</span><br><span class="line">        &#125;).thenRun(() -&gt; &#123;</span><br><span class="line">            <span class="keyword">try</span> &#123; TimeUnit.MILLISECONDS.sleep(<span class="number">20</span>); &#125; <span class="keyword">catch</span> (InterruptedException e) &#123; e.printStackTrace(); &#125;</span><br><span class="line">            System.out.println(<span class="string">&quot;4号任务\t&quot;</span> + Thread.currentThread().getName());</span><br><span class="line">        &#125;);</span><br><span class="line">        System.out.println(completableFuture.get(<span class="number">2L</span>, TimeUnit.SECONDS));</span><br><span class="line">    &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">        threadPool.shutdown();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// output</span></span><br><span class="line"><span class="number">1</span>号任务	pool-<span class="number">1</span>-thread-<span class="number">1</span></span><br><span class="line"><span class="number">2</span>号任务	pool-<span class="number">1</span>-thread-<span class="number">1</span></span><br><span class="line"><span class="number">3</span>号任务	ForkJoinPool.commonPool-worker-<span class="number">1</span></span><br><span class="line"><span class="number">4</span>号任务	ForkJoinPool.commonPool-worker-<span class="number">1</span></span><br><span class="line"><span class="literal">null</span></span><br></pre></td></tr></table></figure></li>
</ol>
<h2 id="4-Java-”锁“-事"><a href="#4-Java-”锁“-事" class="headerlink" title="4. Java ”锁“ 事"></a>4. Java ”锁“ 事</h2><ol>
<li><p>乐观锁 VS 悲观锁</p>
<ul>
<li><p>乐观锁：认为使用数据时不会有其他线程修改数据，因此获取数据时不加锁（适合读操作多）</p>
<p>乐观锁用<code>无锁编程</code>实现，两种实现方式：版本号机制<code>Version</code>、CAS 算法（最常用）</p>
<p>Java 原子类中的递增操作就通过 CAS 自旋实现</p>
</li>
<li><p>悲观锁：认为使用数据时会有其他线程修改数据，因此获取数据时加锁（适合写操作多）</p>
</li>
<li><p><code>synchronized、Lock</code> 的实现类都是悲观锁</p>
</li>
</ul>
</li>
<li><p><a target="_blank" rel="noopener" href="https://www.bilibili.com/video/BV1ar4y1x727?p=32">类锁 VS 对象锁（8 锁案例）</a></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Phone</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">synchronized</span> <span class="keyword">void</span> <span class="title function_">sendEmail</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">try</span> &#123; TimeUnit.SECONDS.sleep(<span class="number">3</span>); &#125; <span class="keyword">catch</span> (InterruptedException e) &#123; e.printStackTrace(); &#125;</span><br><span class="line">        System.out.println(<span class="string">&quot;----sendEmail&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">synchronized</span> <span class="keyword">void</span> <span class="title function_">sendSMS</span><span class="params">()</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;----sendSMS&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">hello</span><span class="params">()</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;----hello&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 8锁案例演示（a 线程调用发邮件、b 线程调用发 短信/hello，先启动 a 线程，后启动 b 线程）</span></span><br><span class="line"><span class="comment">// 1.2 个普通同步方法，标准访问，先打印邮件还是短信</span></span><br><span class="line"><span class="comment">// 按顺序，邮件-短信</span></span><br><span class="line"><span class="comment">// 2.2 个普通同步方法，sendEmail 中加入 3 秒暂停——按</span></span><br><span class="line"><span class="comment">// 对象锁，邮件-短信</span></span><br><span class="line"><span class="comment">// 3.2 个普通同步方法，sendEmail 中加入 3 秒暂停（以下案例均是），添加普通方法 hello</span></span><br><span class="line"><span class="comment">// hello-邮件</span></span><br><span class="line"><span class="comment">// 4.两部手机，由两个线程分别访问</span></span><br><span class="line"><span class="comment">// 短信-邮件</span></span><br><span class="line"><span class="comment">// 5.2 个静态同步方法，有 1 部手机</span></span><br><span class="line"><span class="comment">// 类锁，邮件-短信</span></span><br><span class="line"><span class="comment">// 6.2 个静态同步方法，有 2 部手机</span></span><br><span class="line"><span class="comment">// 类锁，邮件-短信</span></span><br><span class="line"><span class="comment">// 7.1 个静态同步方法，1 个普通同步方法，有 1 部手机</span></span><br><span class="line"><span class="comment">// 类锁和对象锁互不干涉，短信-邮件</span></span><br><span class="line"><span class="comment">// 8.1 个静态同步方法，1 个普通同步方法，有 2 部手机</span></span><br><span class="line"><span class="comment">// 类锁和对象锁互不干涉，短信-邮件</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Demo</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span>  &#123;</span><br><span class="line"></span><br><span class="line">        <span class="type">Phone</span> <span class="variable">phone1</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Phone</span>();</span><br><span class="line">        <span class="type">Phone</span> <span class="variable">phone2</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Phone</span>();</span><br><span class="line"></span><br><span class="line">        <span class="keyword">new</span> <span class="title class_">Thread</span>(() -&gt; &#123;</span><br><span class="line">            phone1.sendEmail();</span><br><span class="line">        &#125;, <span class="string">&quot;a&quot;</span>).start();</span><br><span class="line"></span><br><span class="line">        <span class="keyword">try</span> &#123; TimeUnit.SECONDS.sleep(<span class="number">1</span>); &#125; <span class="keyword">catch</span> (InterruptedException e) &#123; e.printStackTrace(); &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">new</span> <span class="title class_">Thread</span>(() -&gt; &#123;</span><br><span class="line">            phone1.sendSMS();</span><br><span class="line">            phone1.hello();</span><br><span class="line">            phone2.sendSMS();</span><br><span class="line">        &#125;, <span class="string">&quot;b&quot;</span>).start();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
</li>
<li><p><code>synchronized</code> 字节码和底层原理<strong>（<a href="#chapter12">详见 12 章 synchronized 锁升级</a>）</strong></p>
<ul>
<li><p><code>synchronized</code> 可实现<code>代码块锁、实例对象锁、类锁</code></p>
</li>
<li><p>字节码分析</p>
<ul>
<li><p>代码块锁：<code>monitorenter-monitorexit(正常退出)-monitorexit(异常退出)</code></p>
</li>
<li><p>实例对象锁（普通同步方法）：<code>ACC_SYNCHRONIZED</code> 标志位</p>
<p>如标志位被设置则执行线程时会先持有 <code>monitor</code> 锁再执行该方法</p>
</li>
<li><p>类锁（静态同步方法）：<code>ACC_STATIC、ACC_SYNCHRONIZED</code> 标志位</p>
<p><code>ACC_STATIC</code> 用于区分普通与静态方法</p>
</li>
</ul>
</li>
<li><p>底层原理：为什么所有对象都可以成为一个锁？</p>
<ul>
<li>在 <code>HotSpot</code> 虚拟机中，<code>monitor</code> 采用 <code>ObjectMonitor</code> 实现</li>
<li><code>ObjectMonitor.java</code> - <code>ObjectMonitor.cpp</code> - <code>ObjectMonitor.hpp</code> </li>
<li>每个对象都天生自带一个 <code>monitor</code> 对象，每一个被锁住的对象都会和 <code>Monitor</code> 关联起来</li>
<li><code>ObjectMonitor</code> 关键属性<ul>
<li>_owner：指向持有 <code>ObjectMonitor</code> 对象的线程</li>
<li>_WaitSet：存放处于 wait 状态的线程队列（存放已获得过锁但调用<code>wait()</code>的线程）</li>
<li>_EntryList：存放处于等待锁 block 状态的线程队列（存放未获得锁的线程）</li>
<li>_recursions：锁的重入次数（用来记录该线程获取锁的次数）</li>
<li>_count：初始值为 0，表当前对象未锁定。每次加锁 +1，释放锁 -1</li>
</ul>
</li>
</ul>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">1.有两个线程，线程A、线程B将竞争锁访问同步代码块，先进入ObjectMonitor的EntrySet中等待锁</span><br><span class="line"></span><br><span class="line">2.当CPU调度线程A获取到锁则进入同步代码，ObjectMonitor owner属性指向线程A，线程B继续在EntryList中等待</span><br><span class="line"></span><br><span class="line">3.线程A在同步代码中执行wait，则线程进入WaitSet并释放锁，ObjectMonitor owner属性清空</span><br><span class="line"></span><br><span class="line">4.CPU调度使线程B获取到锁进入同步代码块，ObjectMonitor owner属性指向线程B，任务执行完退出同步代码之前调用notifyAll，线程A被唤醒，从WaitSet转到EntryList中等待锁，线程B退出同步代码块，ObjectMonitor owner属性清空；</span><br><span class="line"></span><br><span class="line">5.CPU调度使线程A获取同步锁，继续后续代码；</span><br></pre></td></tr></table></figure>
</li>
<li><p>synchronized 流程图（<a href="#synchronized%E6%B5%81%E7%A8%8B3">流程图扩展</a>）</p>
<p><img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Java/JUC/synchronized%E6%B5%81%E7%A8%8B1.png" alt="synchronized流程1"></p>
<p><img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Java/JUC/synchronized%E6%B5%81%E7%A8%8B2.png" alt="synchronized流程2"></p>
</li>
</ul>
</li>
<li><p>公平锁和非公平锁<strong>（<a href="#chapter13">底层详见第 13 章 AQS</a>）</strong></p>
<ul>
<li><p>非公平锁（常用）：各个线程获得锁的概率不相等，大概率某一两个线程一直持有锁</p>
<p><code>synchronized、ReentrantLock</code> 都是非公平锁，<code>ReentrantLock</code> 可以设置为公平锁</p>
<ul>
<li>优点：效率高、切换线程少（消耗资源少）</li>
<li>缺点：优先级反转、饥饿</li>
</ul>
</li>
<li><p>公平锁：随着时间推移，各个线程获得锁的概率相等</p>
<p>优缺点反，适用于对效率要求不高，重在公平</p>
</li>
</ul>
</li>
<li><p>可重入锁（递归锁）</p>
<ul>
<li><p>概念：指同一个线程在外层获取锁时，再进入该线程的内层方法会自动获得该锁</p>
</li>
<li><p>synchronized 默认可重入，可重入实现原理：</p>
<ul>
<li><code>ObjectMonitor</code> 中的 <code>_recursions</code> 关键字</li>
</ul>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 用同步代码块示例</span></span><br><span class="line"><span class="comment">// （普通、静态）同步方法递归调用即可</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span>  &#123;</span><br><span class="line">    <span class="keyword">final</span> <span class="type">Object</span> <span class="variable">object</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Object</span>();</span><br><span class="line">    <span class="keyword">new</span> <span class="title class_">Thread</span>(() -&gt; &#123;</span><br><span class="line">        <span class="keyword">synchronized</span> (object) &#123;</span><br><span class="line">            System.out.println(<span class="string">&quot;外层锁&quot;</span>);</span><br><span class="line">            <span class="keyword">synchronized</span> (object) &#123;</span><br><span class="line">                System.out.println(<span class="string">&quot;中层锁&quot;</span>);</span><br><span class="line">                <span class="keyword">synchronized</span> (object) &#123;</span><br><span class="line">                    System.out.println(<span class="string">&quot;内层锁&quot;</span>);</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;, <span class="string">&quot;t1&quot;</span>).start();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// output</span></span><br><span class="line">外层锁</span><br><span class="line">中层锁</span><br><span class="line">内层锁</span><br></pre></td></tr></table></figure>
</li>
<li><p><code>ReentrantLock</code> 实现可重入锁要显示释放（否则会阻塞其他线程）</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span>  &#123;</span><br><span class="line">    <span class="type">Lock</span> <span class="variable">lock</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ReentrantLock</span>();</span><br><span class="line">    <span class="keyword">new</span> <span class="title class_">Thread</span>(() -&gt; &#123;</span><br><span class="line">        lock.lock();</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            System.out.println(<span class="string">&quot;外层锁&quot;</span>);</span><br><span class="line">            lock.lock();</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                System.out.println(<span class="string">&quot;内层锁&quot;</span>);</span><br><span class="line">            &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">                <span class="comment">// 删掉这个 unlock 输出相同，但会阻塞其他线程</span></span><br><span class="line">                lock.unlock();</span><br><span class="line">            &#125;</span><br><span class="line">        &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">            <span class="comment">// 删掉这个 unlock 输出相同，但会阻塞其他线程</span></span><br><span class="line">            lock.unlock();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;, <span class="string">&quot;t1&quot;</span>).start();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// output</span></span><br><span class="line">外层锁</span><br><span class="line">内层锁</span><br></pre></td></tr></table></figure></li>
</ul>
</li>
<li><p>死锁及排查</p>
<ul>
<li><p>概念：指两个及以上线程在执行过程中因争夺资源而造成的一种互相等待的现象</p>
<p><img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Java/JUC/%E6%AD%BB%E9%94%81%E7%A4%BA%E6%84%8F%E5%9B%BE.png" alt="死锁示意图"></p>
</li>
<li><p>死锁示例代码</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span>  &#123;</span><br><span class="line">    <span class="type">Object</span> <span class="variable">o1</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Object</span>();</span><br><span class="line">    <span class="type">Object</span> <span class="variable">o2</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Object</span>();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">new</span> <span class="title class_">Thread</span>(() -&gt; &#123;</span><br><span class="line">        <span class="keyword">synchronized</span> (o1) &#123;</span><br><span class="line">            System.out.println(<span class="string">&quot;t1 get o1&#x27;s lock and wants o2&#x27;s lock&quot;</span>);</span><br><span class="line">            <span class="keyword">try</span> &#123; TimeUnit.SECONDS.sleep(<span class="number">1</span>); &#125; <span class="keyword">catch</span> (InterruptedException e) &#123; e.printStackTrace(); &#125;</span><br><span class="line">            <span class="keyword">synchronized</span> (o2) &#123;</span><br><span class="line">                System.out.println(<span class="string">&quot;t1 get o2&#x27;s lock&quot;</span>);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;, <span class="string">&quot;t1&quot;</span>).start();</span><br><span class="line">    <span class="keyword">new</span> <span class="title class_">Thread</span>(() -&gt; &#123;</span><br><span class="line">        <span class="keyword">synchronized</span> (o2) &#123;</span><br><span class="line">            System.out.println(<span class="string">&quot;t2 get o2&#x27;s lock and wants o1&#x27;s lock&quot;</span>);</span><br><span class="line">            <span class="keyword">try</span> &#123; TimeUnit.SECONDS.sleep(<span class="number">1</span>); &#125; <span class="keyword">catch</span> (InterruptedException e) &#123; e.printStackTrace(); &#125;</span><br><span class="line">            <span class="keyword">synchronized</span> (o1) &#123;</span><br><span class="line">                System.out.println(<span class="string">&quot;t2 get o1&#x27;s lock&quot;</span>);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;, <span class="string">&quot;t2&quot;</span>).start();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// output</span></span><br><span class="line">t1 get o1<span class="string">&#x27;s lock and wants o2&#x27;</span>s lock</span><br><span class="line">t2 get o2<span class="string">&#x27;s lock and wants o1&#x27;</span>s lock</span><br></pre></td></tr></table></figure>
</li>
<li><p>死锁检查的两种方法：</p>
<ul>
<li>Java 原生 <code>jstack</code> 命令</li>
<li><code>jconsole</code> 控制台</li>
</ul>
</li>
</ul>
</li>
<li><p>写锁（独占锁）VS 读锁（共享锁）<strong>（<a href="#chapter14">详见第 14 章 StampedLock</a> ）</strong></p>
</li>
<li><p>自旋锁 SpinLock<strong>（<a href="#Spinlock">详见第 8 章 CAS</a>）</strong></p>
</li>
<li><p>无锁 - 独占锁 - 读写锁- 邮戳锁<strong>（<a href="#chapter14">详见第 14 章 StampedLock</a> ）</strong></p>
</li>
<li><p>无锁- 偏向锁 - 轻量锁 - 重量锁<strong>（<a href="#chapter12">详见第 12 章 synchronized 锁升级</a>）</strong></p>
</li>
</ol>
<h2 id="5-LockSupport-与线程中断"><a href="#5-LockSupport-与线程中断" class="headerlink" title="5. LockSupport 与线程中断"></a>5. LockSupport 与线程中断</h2><ol>
<li><p>线程中断机制说明</p>
<ul>
<li>一个线程不应该由其他线程来强制中断或停止，而应该由线程自行控制。<code>Thread.stop、Thread.suspend、Thread.resume</code> 都已经被废弃</li>
<li>Java 中没有办法立即停止一个线程，所以提供了<strong>线程停止的协商机制——中断</strong></li>
<li><strong>中断标识协商机制</strong>，Java没有给中断增加任何语法，需要手动实现</li>
</ul>
</li>
<li><p>线程中断三大方法</p>
<ul>
<li><p><code>void interrupt()</code>：中断<code>调用此方法的线程</code>（设置中断标志位）</p>
<ul>
<li><p>仅仅是将中断状态设置为 true，发起一个协商而不是立刻停止</p>
</li>
<li><p>底层调用 <code>native void interrupt0()</code></p>
</li>
<li><p>如果当前线程正在调用 <code>wait()、join()、sleep()</code> 等方法（即处于<code>被阻塞状态</code>）时其他线程调用该线程的<code>interrupt()</code>方法，<strong>则当前线程的中断状态被清除，并抛出 <code>InterruptedException</code></strong></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">    <span class="type">Thread</span> <span class="variable">t1</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Thread</span>(() -&gt; &#123;</span><br><span class="line">        <span class="keyword">while</span> (<span class="literal">true</span>) &#123;</span><br><span class="line">            <span class="keyword">if</span> (Thread.currentThread().isInterrupted()) &#123;</span><br><span class="line">                System.out.println(<span class="string">&quot;t1 终止，中断标志位为: &quot;</span> + Thread.currentThread().isInterrupted());</span><br><span class="line">                <span class="keyword">break</span>;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                TimeUnit.SECONDS.sleep(<span class="number">1</span>);</span><br><span class="line">            &#125; <span class="keyword">catch</span> (InterruptedException e) &#123;</span><br><span class="line">                <span class="comment">// 此处不调用interrupt则会死循环!!!!!!!!!</span></span><br><span class="line">                <span class="comment">// 因为抛出异常后中断状态被清除</span></span><br><span class="line">                Thread.currentThread().interrupt();</span><br><span class="line">                e.printStackTrace();</span><br><span class="line">            &#125;</span><br><span class="line">            System.out.println(<span class="string">&quot;t1 线程未终止&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;, <span class="string">&quot;t1&quot;</span>);</span><br><span class="line">    t1.start();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        TimeUnit.SECONDS.sleep(<span class="number">1</span>);</span><br><span class="line">    &#125; <span class="keyword">catch</span> (InterruptedException e) &#123;</span><br><span class="line">        e.printStackTrace();</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">new</span> <span class="title class_">Thread</span>(() -&gt; &#123;</span><br><span class="line">        t1.interrupt();</span><br><span class="line">    &#125;, <span class="string">&quot;t2&quot;</span>).start();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// output</span></span><br><span class="line">t1 线程未终止</span><br><span class="line">t1 线程未终止</span><br><span class="line">java.lang.InterruptedException: sleep interrupted</span><br><span class="line">	at java.lang.Thread.sleep(Native Method)</span><br><span class="line">	at java.lang.Thread.sleep(Thread.java:<span class="number">340</span>)</span><br><span class="line">	at java.util.concurrent.TimeUnit.sleep(TimeUnit.java:<span class="number">386</span>)</span><br><span class="line">	at Demo.lambda$main$<span class="number">0</span>(Demo.java:<span class="number">17</span>)</span><br><span class="line">	at java.lang.Thread.run(Thread.java:<span class="number">748</span>)</span><br></pre></td></tr></table></figure>
</li>
<li><p>中断不活动的线程不会产生任何影响<strong>（代码见下：中断标识位为 true，线程是否立刻停止）</strong></p>
</li>
</ul>
</li>
<li><p><code>static boolean interrupted()</code>：测试<code>当前线程</code>是否已被中断，并清除当前中断状态（重置为 false）</p>
<ul>
<li>连续两次调用此方法，则第二次一定返回 false</li>
<li>底层调用 <code>native boolean isInterrupted(boolean ClearInterrupted)</code>，其中参数 <code>ClearInterrupted = true</code>，即重置中断标志位为 false</li>
</ul>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">    System.out.println(Thread.currentThread().getName() + <span class="string">&quot;\t&quot;</span> + Thread.interrupted());</span><br><span class="line">    System.out.println(Thread.currentThread().getName() + <span class="string">&quot;\t&quot;</span> + Thread.interrupted());</span><br><span class="line">    System.out.println(<span class="string">&quot;111&quot;</span>);</span><br><span class="line">    Thread.currentThread().interrupt();</span><br><span class="line">    System.out.println(<span class="string">&quot;222&quot;</span>);</span><br><span class="line">    System.out.println(Thread.currentThread().getName() + <span class="string">&quot;\t&quot;</span> + Thread.interrupted());</span><br><span class="line">    System.out.println(Thread.currentThread().getName() + <span class="string">&quot;\t&quot;</span> + Thread.interrupted());</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// output</span></span><br><span class="line">main	<span class="literal">false</span></span><br><span class="line">main	<span class="literal">false</span></span><br><span class="line"><span class="number">111</span></span><br><span class="line"><span class="number">222</span></span><br><span class="line">main	<span class="literal">true</span></span><br><span class="line">main	<span class="literal">false</span></span><br></pre></td></tr></table></figure>
</li>
<li><p><code>boolean isInterrupted()</code>：测试<code>调用此方法的线程</code>是否已被中断（检查中断标志位）</p>
<ul>
<li>底层调用 <code>native boolean isInterrupted(boolean ClearInterrupted)</code>，其中参数 <code>ClearInterrupted = false</code>，即不重置中断标志位</li>
</ul>
</li>
</ul>
</li>
<li><p>如何中断运行中的线程</p>
<ul>
<li><p><code>volatile</code></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">static</span> <span class="keyword">volatile</span> <span class="type">boolean</span> <span class="variable">isStop</span> <span class="operator">=</span> <span class="literal">false</span>;</span><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span>  &#123;</span><br><span class="line">    <span class="keyword">new</span> <span class="title class_">Thread</span>(() -&gt; &#123;</span><br><span class="line">        <span class="keyword">while</span> (<span class="literal">true</span>) &#123;</span><br><span class="line">            <span class="keyword">if</span>(isStop) &#123;</span><br><span class="line">                System.out.println(<span class="string">&quot;isStop的值被修改为true, t1停止&quot;</span>);</span><br><span class="line">                <span class="keyword">break</span>;</span><br><span class="line">            &#125;</span><br><span class="line">            System.out.println(<span class="string">&quot;t1 hello volatile&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;, <span class="string">&quot;t1&quot;</span>).start();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">try</span> &#123; TimeUnit.MILLISECONDS.sleep(<span class="number">3</span>); &#125; <span class="keyword">catch</span> (InterruptedException e) &#123; e.printStackTrace(); &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">new</span> <span class="title class_">Thread</span>(() -&gt; &#123;</span><br><span class="line">        isStop = <span class="literal">true</span>;</span><br><span class="line">    &#125;, <span class="string">&quot;t2&quot;</span>).start();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// output</span></span><br><span class="line">......</span><br><span class="line">t1 hello <span class="keyword">volatile</span></span><br><span class="line">t1 hello <span class="keyword">volatile</span></span><br><span class="line">t1 hello <span class="keyword">volatile</span></span><br><span class="line">isStop的值被修改为<span class="literal">true</span>, t1停止</span><br></pre></td></tr></table></figure>
</li>
<li><p><code>AtomicBoolean</code></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">static</span> <span class="type">AtomicBoolean</span> <span class="variable">atomicBoolean</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">AtomicBoolean</span>(<span class="literal">false</span>);</span><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span>  &#123;</span><br><span class="line">    <span class="keyword">new</span> <span class="title class_">Thread</span>(() -&gt; &#123;</span><br><span class="line">        <span class="keyword">while</span> (<span class="literal">true</span>) &#123;</span><br><span class="line">            <span class="keyword">if</span>(atomicBoolean.get()) &#123;</span><br><span class="line">                System.out.println(<span class="string">&quot;atomicBoolean.value被修改为true, t1停止&quot;</span>);</span><br><span class="line">                <span class="keyword">break</span>;</span><br><span class="line">            &#125;</span><br><span class="line">            System.out.println(<span class="string">&quot;t1 hello atomicBoolean&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;, <span class="string">&quot;t1&quot;</span>).start();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">try</span> &#123; TimeUnit.MILLISECONDS.sleep(<span class="number">3</span>); &#125; <span class="keyword">catch</span> (InterruptedException e) &#123; e.printStackTrace(); &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">new</span> <span class="title class_">Thread</span>(() -&gt; &#123;</span><br><span class="line">        atomicBoolean.set(<span class="literal">true</span>);</span><br><span class="line">    &#125;, <span class="string">&quot;t2&quot;</span>).start();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// output</span></span><br><span class="line">......</span><br><span class="line">t1 hello atomicBoolean</span><br><span class="line">t1 hello atomicBoolean</span><br><span class="line">t1 hello atomicBoolean</span><br><span class="line">atomicBoolean.value被修改为<span class="literal">true</span>, t1停止</span><br></pre></td></tr></table></figure>
</li>
<li><p>线程中断 API：<code>interrupt()、isInterrupted()</code></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span>  &#123;</span><br><span class="line">    <span class="type">Thread</span> <span class="variable">t1</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Thread</span>(() -&gt; &#123;</span><br><span class="line">        <span class="keyword">while</span> (<span class="literal">true</span>) &#123;</span><br><span class="line">            <span class="keyword">if</span> (Thread.currentThread().isInterrupted()) &#123;</span><br><span class="line">                System.out.println(<span class="string">&quot;t1 isInterrupted()被修改为true, t1停止&quot;</span>);</span><br><span class="line">                <span class="keyword">break</span>;</span><br><span class="line">            &#125;</span><br><span class="line">            System.out.println(<span class="string">&quot;t1 hello isInterrupted()&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;, <span class="string">&quot;t1&quot;</span>);</span><br><span class="line">    t1.start();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">try</span> &#123; TimeUnit.MILLISECONDS.sleep(<span class="number">3</span>); &#125; <span class="keyword">catch</span> (InterruptedException e) &#123; e.printStackTrace(); &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">new</span> <span class="title class_">Thread</span>(() -&gt; &#123;</span><br><span class="line">        t1.interrupt();</span><br><span class="line">    &#125;, <span class="string">&quot;t2&quot;</span>).start();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// output</span></span><br><span class="line">......</span><br><span class="line">t1 hello <span class="title function_">isInterrupted</span><span class="params">()</span></span><br><span class="line">t1 hello <span class="title function_">isInterrupted</span><span class="params">()</span></span><br><span class="line">t1 hello <span class="title function_">isInterrupted</span><span class="params">()</span></span><br><span class="line">t1 <span class="title function_">isInterrupted</span><span class="params">()</span>被修改为<span class="literal">true</span>, t1停止</span><br></pre></td></tr></table></figure></li>
</ul>
</li>
<li><p>中断标识位为 true，线程是否立刻停止<strong>（否）</strong></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span>  &#123;</span><br><span class="line">    <span class="type">Thread</span> <span class="variable">t1</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Thread</span>(() -&gt; &#123;</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; <span class="number">300</span>; i++) &#123;</span><br><span class="line">            System.out.println(<span class="string">&quot;---- &quot;</span> + i);</span><br><span class="line">        &#125;</span><br><span class="line">        System.out.println(<span class="string">&quot;t1线程interrupt()后的中断标识02 &quot;</span> + Thread.currentThread().isInterrupted());</span><br><span class="line">    &#125;, <span class="string">&quot;t1&quot;</span>);</span><br><span class="line">    t1.start();</span><br><span class="line"></span><br><span class="line">    System.out.println(<span class="string">&quot;t1线程的默认中断标识为 &quot;</span> + t1.isInterrupted());</span><br><span class="line">    <span class="keyword">try</span> &#123; TimeUnit.MILLISECONDS.sleep(<span class="number">1</span>); &#125; <span class="keyword">catch</span> (InterruptedException e) &#123; e.printStackTrace(); &#125;</span><br><span class="line">    t1.interrupt();</span><br><span class="line">    System.out.println(<span class="string">&quot;t1线程interrupt()后的中断标识01 &quot;</span> + t1.isInterrupted());</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 2秒后打印已完成，t1线程已结束。中断标志位重置为false，且设置interrupt也不修改中断标志位</span></span><br><span class="line">    <span class="keyword">try</span> &#123; TimeUnit.SECONDS.sleep(<span class="number">2</span>); &#125; <span class="keyword">catch</span> (InterruptedException e) &#123; e.printStackTrace(); &#125;</span><br><span class="line">    t1.interrupt();</span><br><span class="line">    System.out.println(<span class="string">&quot;t1线程interrupt()后的中断标识03 &quot;</span> + t1.isInterrupted());</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// output</span></span><br><span class="line">t1线程的默认中断标识为 <span class="literal">false</span></span><br><span class="line">......</span><br><span class="line">---- <span class="number">172</span></span><br><span class="line">t1线程interrupt()后的中断标识<span class="number">01</span> <span class="literal">true</span></span><br><span class="line">---- <span class="number">173</span></span><br><span class="line">......</span><br><span class="line">---- <span class="number">299</span></span><br><span class="line">t1线程interrupt()后的中断标识<span class="number">02</span> <span class="literal">true</span></span><br><span class="line">t1线程interrupt()后的中断标识<span class="number">03</span> <span class="literal">false</span></span><br></pre></td></tr></table></figure>
</li>
<li><p>LockSupport 是 Java 包 <code>java.util.concurrent.locks</code> 下的一个类</p>
<ul>
<li><p>用于创建锁和其他同步类的基本线程阻塞原语</p>
</li>
<li><p>无构造方法</p>
<p><img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Java/JUC/LockSupport.png" alt="LockSupport"></p>
</li>
<li><p>两个主要方法</p>
<ul>
<li>park：消费许可证</li>
<li>unpack：获取许可证（许可证不能累加，最多只有一个）</li>
</ul>
</li>
<li><p><strong>线程等待唤醒机制（注意三种实现的特征，思考三次迭代的原因）</strong></p>
<ul>
<li><p><code>Object</code> 类中的 <code>wait + notify + synchronized</code></p>
<ul>
<li><p>一定要在 synchronized 代码块中使用，否则抛异常</p>
</li>
<li><p>一定要先 wait 再 notify，否则唤醒无效</p>
</li>
<li><p><strong>特点：notify 无法唤醒指定线程，多个线程会有竞争</strong></p>
</li>
<li><p><code>notify</code> 方法随机唤醒对象的等待池中的一个线程，进入锁池；<code>notifyAll</code> 唤醒对象的等待池中的所有线程，进入锁池</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">等待池：假设一个线程 A 调用了某个对象的 wait() 方法，线程 A 就会释放该对象的锁后，进入到了该对象的等待池，等待池中的线程不会去竞争该对象的锁</span><br><span class="line"></span><br><span class="line">锁池：只有获取了对象的锁，线程才能执行对象的 synchronized 代码，对象的锁每次只有一个线程可以获得，其他线程只能在锁池中等待</span><br></pre></td></tr></table></figure></li>
</ul>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">    <span class="type">Object</span> <span class="variable">objectLock</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Object</span>();</span><br><span class="line">    <span class="keyword">new</span> <span class="title class_">Thread</span>(() -&gt; &#123;</span><br><span class="line">        <span class="keyword">synchronized</span> (objectLock) &#123;</span><br><span class="line">            System.out.println(<span class="string">&quot;t1 come in&quot;</span>);</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                objectLock.wait();</span><br><span class="line">            &#125; <span class="keyword">catch</span> (InterruptedException e) &#123;</span><br><span class="line">                e.printStackTrace();</span><br><span class="line">            &#125;</span><br><span class="line">            System.out.println(<span class="string">&quot;ti wake up&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;, <span class="string">&quot;t1&quot;</span>).start();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">try</span> &#123; TimeUnit.SECONDS.sleep(<span class="number">1</span>); &#125; <span class="keyword">catch</span> (InterruptedException e) &#123; e.printStackTrace(); &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">new</span> <span class="title class_">Thread</span>(() -&gt; &#123;</span><br><span class="line">        <span class="keyword">synchronized</span> (objectLock) &#123;</span><br><span class="line">            objectLock.notify();</span><br><span class="line">            System.out.println(<span class="string">&quot;t2 notify&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;, <span class="string">&quot;t2&quot;</span>).start();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// output</span></span><br><span class="line">t1 come in</span><br><span class="line">t2 notify</span><br><span class="line">ti wake up</span><br></pre></td></tr></table></figure>
</li>
<li><p><code>JUC</code> 包中的 <code>await + signal + Lock Condition</code></p>
<ul>
<li>一定要在 lock 锁块中使用，否则抛异常</li>
<li>一定要先 await 再 signal，否则唤醒无效</li>
<li>特点：可以根据 condition 唤醒指定线程</li>
</ul>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">    <span class="type">ReentrantLock</span> <span class="variable">lock</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ReentrantLock</span>();</span><br><span class="line">    <span class="type">Condition</span> <span class="variable">condition</span> <span class="operator">=</span> lock.newCondition();</span><br><span class="line">    <span class="keyword">new</span> <span class="title class_">Thread</span>(() -&gt; &#123;</span><br><span class="line">        lock.lock();</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            System.out.println(<span class="string">&quot;t1 come in&quot;</span>);</span><br><span class="line">            condition.await();</span><br><span class="line">            System.out.println(<span class="string">&quot;ti wake up&quot;</span>);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (InterruptedException e) &#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">        &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">            lock.unlock();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;, <span class="string">&quot;t1&quot;</span>).start();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">try</span> &#123; TimeUnit.SECONDS.sleep(<span class="number">1</span>); &#125; <span class="keyword">catch</span> (InterruptedException e) &#123; e.printStackTrace(); &#125;</span><br><span class="line">    <span class="keyword">new</span> <span class="title class_">Thread</span>(() -&gt; &#123;</span><br><span class="line">        lock.lock();</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            condition.signal();</span><br><span class="line">            System.out.println(<span class="string">&quot;t2 signal&quot;</span>);</span><br><span class="line">        &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">            lock.unlock();</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">    &#125;, <span class="string">&quot;t2&quot;</span>).start();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// output</span></span><br><span class="line">t1 come in</span><br><span class="line">t2 signal</span><br><span class="line">ti wake up</span><br></pre></td></tr></table></figure>
</li>
<li><p><code>LockSupport</code> 中的 <code>pack + unpack</code></p>
<ul>
<li><strong>无需在锁块中使用</strong></li>
<li><strong>pack 和 unpack 的使用无顺序限制（许可证无法累加，成对使用）</strong></li>
</ul>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">    <span class="type">Thread</span> <span class="variable">t1</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Thread</span>(() -&gt; &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;t1 come in&quot;</span>);</span><br><span class="line">        <span class="comment">// 如此处有两个 park，则无论 t2 有多少个 unpack 都会被阻塞</span></span><br><span class="line">        LockSupport.park();</span><br><span class="line">        System.out.println(<span class="string">&quot;t1 wake up&quot;</span>);</span><br><span class="line">    &#125;, <span class="string">&quot;t1&quot;</span>);</span><br><span class="line">    t1.start();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">try</span> &#123; TimeUnit.SECONDS.sleep(<span class="number">1</span>); &#125; <span class="keyword">catch</span> (InterruptedException e) &#123; e.printStackTrace(); &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">new</span> <span class="title class_">Thread</span>(() -&gt; &#123;</span><br><span class="line">        <span class="comment">// 给 t1 线程发许可证</span></span><br><span class="line">        LockSupport.unpark(t1);</span><br><span class="line">        System.out.println(<span class="string">&quot;t2 unpack&quot;</span>);</span><br><span class="line">    &#125;, <span class="string">&quot;t2&quot;</span>).start();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// output</span></span><br><span class="line">t1 come in</span><br><span class="line">t2 unpack</span><br><span class="line">t1 wake up</span><br></pre></td></tr></table></figure></li>
</ul>
</li>
</ul>
</li>
</ol>
<h2 id="6-Java-内存模型-JMM"><a href="#6-Java-内存模型-JMM" class="headerlink" title="6. Java 内存模型 - JMM"></a>6. Java 内存模型 - JMM</h2><ol>
<li><p><code>JMM == Java 内存模型</code> VS <code>JVM == Java 虚拟机</code></p>
</li>
<li><p>JMM 作用：</p>
<ul>
<li>实现线程和主内存之间的抽象关系</li>
<li>屏蔽各个硬件平台和操作系统的内存访问差异，使 Java 程序在各种平台下都能达到一致的内存访问效果</li>
</ul>
</li>
<li><p>JMM 三大特性</p>
<ul>
<li><p>原子性：一个操作不可打断，全做 or 全不做</p>
</li>
<li><p>可见性：一个线程修改某个共享变量时立即写回内存，使其对于其他线程可见</p>
<p>防止脏读</p>
 <img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Java/JUC/%E5%8F%AF%E8%A7%81%E6%80%A7.png" alt="可见性" style="zoom:60%;" />
</li>
<li><p>有序性：编译器和处理器可能对指令进行重排序，因此在不影响结果（保持语义一致性）的情况下，指令顺序和代码顺序可能不一致。需考虑数据依赖性</p>
</li>
</ul>
</li>
<li><p><code>happens-before</code> 原则</p>
<ul>
<li><p>实现可见性和有序性要求遵守 <code>happens-before</code> 原则</p>
</li>
<li><p>若有序性仅靠 <code>volatile、synchronized</code> 完成，则代码过于繁琐。于是 JVM 底层提出  <code>happens-before</code> 原则来判断线程是否安全、数据是否存在竞争</p>
</li>
<li><p>总原则：</p>
<ul>
<li>如果操作 A <code>happens-before</code> 操作 B，那么 A 的执行结果对 B 可见，且 A 发生在 B 之前</li>
<li>如果操作 A 和操作 B 存在 <code>happens-before</code> 关系，但是重排序后的执行结果与原结果一致，则可以重排序（A-B 或 B-A）</li>
</ul>
</li>
<li><p>8 条细则</p>
<ul>
<li>次序规则：同一线程内，按照代码顺序先来后到</li>
<li>锁定规则：持有锁的线程释放锁后，其他线程才能获得锁</li>
<li>volatile 变量规则：前面的写操作先于后面的读操作（时间顺序）</li>
<li>传递规则：A 先于 B，B 先于 C &#x3D;&gt; A 先于 C</li>
<li>线程启动规则：线程的 start 方法先于该线程任意何操作</li>
<li>线程中断规则：中断方法 interrupt 的调用发生在线程感知到被中断之前</li>
<li>线程终止规则：线程的所有操作都先发生于对此线程的终止检测操作 <code>isAlive()</code></li>
<li>对象终结规则：一个对象的初始化先于其 <code>finalize()</code> 方法被调用（被回收时调用该方法）</li>
</ul>
</li>
<li><p>一般将属性设为 volatile 变量，写操作用 synchronized 修饰，读操作正常</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line">Class A &#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">volatile</span> <span class="type">int</span> id;</span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">getId</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> id;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">synchronized</span> <span class="keyword">void</span> <span class="title function_">setId</span><span class="params">(<span class="type">int</span> id)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.id = id;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li>
</ul>
</li>
</ol>
<h2 id="7-volatile-与-JMM"><a href="#7-volatile-与-JMM" class="headerlink" title="7. volatile 与 JMM"></a>7. volatile 与 JMM</h2><ol>
<li><p>volatile 修饰的变量有两大特点：可见性、有序性（不保证原子性）</p>
<ul>
<li>当写 volatile 变量时，JVM 会把该线程对应的本地内存中的共享变量<strong>立刻写回主存</strong></li>
<li>当读 volatile 变量时，JVM 会把该线程对应的本地内存设为无效，<strong>从主存中直接读取该共享变量</strong></li>
</ul>
</li>
<li><p><strong>volatile 内存屏障（面试重点！！！）</strong></p>
<ul>
<li><p><strong>内存屏障是 volatile 实现可见性、有序性的重点</strong></p>
</li>
<li><p>内存屏障 &#x3D;&#x3D; 内存栅栏，是一类同步屏蔽指令，是 CPU 或编译器在对内存随机访问的操作中的一个同步点，使得此点之前的所有读写操作都执行后才可开始此点之后的操作（避免代码重排序）</p>
</li>
<li><p>内存屏障是一种 JVM 指令，Java 重排序要求 <strong>Java 编译器在生成 JVM 指令时插入特定的内存屏障指令</strong>，从而保证可见性和原子性</p>
</li>
<li><p>内存屏障原则：</p>
<ul>
<li><strong>内存屏障之前的所有写操作都要写回到主内存</strong></li>
<li><strong>内存屏障之后的所有读操作都能获得写操作的最新结果（保证可见性）</strong></li>
</ul>
</li>
<li><p>不允许把内存屏障之后的指令重排序到内存屏障之前。即对一个 volatile 变量的写先于任意后续对该变量的读操作（写后读）</p>
</li>
<li><p>内存屏障粗分类：（具体同上<code>内存屏障原则</code>）</p>
<ul>
<li>写屏障：<strong>在写指令后插入写屏障</strong>，强制把写缓冲区的数据刷新回主内存中</li>
<li>读屏障：<strong>在读指令前插入读屏障</strong>，使私有内存或 CPU 缓存中的数据失效，需要重新从主内存读取</li>
</ul>
</li>
<li><p>内存屏障细分类：读读、写写、读写、写读</p>
<p><img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Java/JUC/%E5%86%85%E5%AD%98%E5%B1%8F%E9%9A%9C%E7%BB%86%E5%88%86.png" alt="内存屏障细分"></p>
<ul>
<li><p>在每个 <code>volatile 读</code>后插入 <code>LoadLoad、LoadStore屏障</code></p>
<p><img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Java/JUC/%E8%AF%BB%E5%B1%8F%E9%9A%9C.png" alt="读屏障"></p>
</li>
<li><p>在每个 <code>volatile 写</code>前插入 <code>StoreStore屏障</code>，后插入 <code>StoreLoad屏障</code></p>
<p><img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Java/JUC/%E5%86%99%E5%B1%8F%E9%9A%9C.png" alt="写屏障"></p>
</li>
</ul>
</li>
<li><p>重排序（一般存在数据依赖时禁止重排序）</p>
<ul>
<li>数据依赖：读后写、写后读、写后写</li>
</ul>
<p><img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Java/JUC/%E9%87%8D%E6%8E%92%E5%BA%8F.png" alt="重排序"></p>
</li>
<li><p>禁重排：保证有序性</p>
<p><img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Java/JUC/%E7%A6%81%E9%87%8D%E6%8E%92.png" alt="禁重排"></p>
</li>
<li><p>volatile 变量的读写过程</p>
<p><img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Java/JUC/volatile%E5%8F%98%E9%87%8F%E7%9A%84%E8%AF%BB%E5%86%99%E8%BF%87%E7%A8%8B.png" alt="volatile变量的读写过程"></p>
<p><img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Java/JUC/volatile%E5%8F%98%E9%87%8F%E7%9A%84%E8%AF%BB%E5%86%99%E8%BF%87%E7%A8%8B2.png" alt="volatile变量的读写过程2"></p>
</li>
</ul>
</li>
<li><p>volatile 不保证原子性</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">static</span> <span class="keyword">volatile</span> <span class="type">int</span> <span class="variable">num</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; <span class="number">10</span>; i++) &#123;</span><br><span class="line">        <span class="keyword">new</span> <span class="title class_">Thread</span>(() -&gt; &#123;</span><br><span class="line">            <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">j</span> <span class="operator">=</span> <span class="number">0</span>; j &lt; <span class="number">10000</span>; j++) &#123;</span><br><span class="line">                num++;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;, <span class="string">&quot;t1&quot;</span>).start();</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">try</span> &#123; TimeUnit.SECONDS.sleep(<span class="number">10</span>); &#125; <span class="keyword">catch</span> (InterruptedException e) &#123; e.printStackTrace(); &#125;</span><br><span class="line">    System.out.println(num);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// output</span></span><br><span class="line"><span class="number">91538</span></span><br></pre></td></tr></table></figure>
</li>
<li><p>volatile 适用场景</p>
<ul>
<li><p>标志位，用于保存某个状态的 boolean 或 int 值</p>
</li>
<li><p>适用于单一赋值，不适用于复合赋值（如 i++）</p>
</li>
<li><p>读远多于写时，对于 get 操作不用 synchronized，而用 volatile 修饰变量</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line">Class A &#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">volatile</span> <span class="type">int</span> id;</span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">getId</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> id;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">synchronized</span> <span class="keyword">void</span> <span class="title function_">setId</span><span class="params">(<span class="type">int</span> id)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.id = id;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
</li>
<li><p>DCL 双端锁的发布（单例模式）</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Demo</span> &#123;</span><br><span class="line">	<span class="comment">// 用 volatile 修饰可解决多线程下的安全隐患</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> Demo singleton;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> Demo <span class="title function_">getInstance</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">if</span>(singleton == <span class="literal">null</span>) &#123;</span><br><span class="line">            <span class="keyword">synchronized</span> (Demo.class) &#123;</span><br><span class="line">                <span class="keyword">if</span>(singleton == <span class="literal">null</span>) &#123;			<span class="comment">// 第2步</span></span><br><span class="line">                    <span class="comment">// 多线程环境下，由于重排序，该对象可能还未完成初始化就被其他线程读取</span></span><br><span class="line">               		<span class="comment">// 第2、3步被重排序</span></span><br><span class="line">                    singleton = <span class="keyword">new</span> <span class="title class_">Demo</span>();		<span class="comment">// 第3步</span></span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> singleton;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li>
</ul>
</li>
<li><p>volatile 修饰的变量字节码中会添加标志位 <code>ACC_VOLATILE</code>，JVM 底层会按照 JMM 规范插入内存屏障</p>
</li>
</ol>
<h2 id="8-CAS（compare-and-swap）"><a href="#8-CAS（compare-and-swap）" class="headerlink" title="8. CAS（compare and swap）"></a>8. CAS（compare and swap）</h2><ol>
<li><p>原子类：<code>java.util.concurrent.atomic</code></p>
</li>
<li><p>如何保证 <code>i++</code> 在高并发环境下的正确性</p>
<ul>
<li><p>不使用原子类</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">volatile</span> <span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line"><span class="keyword">public</span> <span class="type">int</span> <span class="title function_">getI</span><span class="params">()</span> &#123;</span><br><span class="line">    <span class="keyword">return</span> i;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">public</span> <span class="keyword">synchronized</span> <span class="keyword">void</span> <span class="title function_">setI</span><span class="params">(<span class="type">int</span> i)</span> &#123;</span><br><span class="line">    <span class="built_in">this</span>.i = i;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
</li>
<li><p>使用原子类</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 构造函数默认初始化为0</span></span><br><span class="line"><span class="type">AtomicInteger</span> <span class="variable">atomicI</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">AtomicInteger</span>();</span><br><span class="line"><span class="keyword">public</span> <span class="type">int</span> <span class="title function_">getAtomicI</span><span class="params">()</span> &#123;</span><br><span class="line">    <span class="keyword">return</span> atomicI.get();</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setAtomicI</span><span class="params">(<span class="type">int</span> atomicI)</span> &#123;</span><br><span class="line">    <span class="built_in">this</span>.atomicI.set(atomicI);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li>
</ul>
</li>
<li><p>CAS 与乐观锁类似</p>
<blockquote>
<p>包含三个操作数：内存位置、预期原值、更新值</p>
<p>执行 CAS 操作时，将<code>内存位置的值</code>与<code>预期原值</code>相比较</p>
<ul>
<li>如果<strong>相等</strong>，则处理器更新该值</li>
<li>如果<strong>不相等</strong>，则不做任何处理</li>
</ul>
<p>多线程同时执行 CAS 操作时只有一个线程会成功</p>
</blockquote>
<p><img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Java/JUC/CAS%E5%8E%9F%E7%90%86%E5%9B%BE.png" alt="CAS原理图"></p>
</li>
<li><p>代码示例</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">    <span class="type">AtomicInteger</span> <span class="variable">atomicInteger</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">AtomicInteger</span>(<span class="number">5</span>);</span><br><span class="line">    System.out.println(atomicInteger.compareAndSet(<span class="number">5</span>, <span class="number">2020</span>) + <span class="string">&quot;\t&quot;</span> + atomicInteger.get());</span><br><span class="line">    System.out.println(atomicInteger.compareAndSet(<span class="number">5</span>, <span class="number">2021</span>) + <span class="string">&quot;\t&quot;</span> + atomicInteger.get());</span><br><span class="line">    System.out.println(atomicInteger.compareAndSet(<span class="number">2021</span>, <span class="number">2023</span>) + <span class="string">&quot;\t&quot;</span> + atomicInteger.get());</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// output</span></span><br><span class="line"><span class="literal">true</span>	<span class="number">2020</span></span><br><span class="line"><span class="literal">false</span>	<span class="number">2020</span></span><br><span class="line"><span class="literal">false</span>	<span class="number">2020</span></span><br></pre></td></tr></table></figure>
</li>
<li><p><a target="_blank" rel="noopener" href="https://www.bilibili.com/video/BV1ar4y1x727/?p=74">源码分析</a></p>
<blockquote>
<p>CAS 是 JDK 提供的<strong>非阻塞原子性操作</strong>，是<strong>通过硬件保证</strong>其比较-更新的原子性</p>
<p>CAS 底层是<strong>原语，其操作连续且不会被打断</strong>，相当于一条 CPU 的原子指令（cmpxchg），CAS API 由 <strong>Unsafe 类</strong>提供</p>
<p>执行 cmpxchg 指令时，会判断当前系统是否为多核系统。如是则给<strong>总线加锁</strong>，只有一个线程能加锁成功，加锁成功后执行 CAS 操作。<strong>即 CAS 的原子性是由 CPU 独占实现的</strong>，比起 synchronized 重量级锁，排他时间短，性能高</p>
</blockquote>
<ul>
<li><p>Unsafe 类</p>
<blockquote>
<p>是 CAS 的核心类，由于 Java 方法无法直接访问底层系统，需要通过 <strong>native 方法</strong>访问</p>
<p>Unsafe 相当于一个后门，基于该类可以直接操作特定内存的数据</p>
<p>Unsafe 类存在于 <code>sun.misc</code> 包中，其内部方法可以<strong>像 C 的指针一样操作内存</strong></p>
</blockquote>
<p><img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Java/JUC/Unsafe%E7%B1%BB.png" alt="Unsafe类"></p>
<blockquote>
<p><strong>1.Unsafe 类中的所有方法都是 native 方法，即都直接调用操作系统底层资源执行相关操作</strong></p>
<p><strong>2.变量 valueOffset 表示该变量值在内存中的偏移地址，Unsafe 类根据内存偏移地址来获取数据</strong></p>
<p><strong>3.变量 value 用 volatile 修饰，保证多线程之间的内存可见性</strong></p>
</blockquote>
</li>
<li><p>流程举例</p>
<p><img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Java/JUC/CAS%E4%B8%BE%E4%BE%8B.png" alt="CAS举例"></p>
</li>
</ul>
</li>
<li><p>原子引用类举例<strong>（<a href="#chapter9">详见第 9 章，原子操作类之 18 罗汉增强</a>）</strong></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="meta">@AllArgsConstructor</span></span><br><span class="line"><span class="meta">@ToString</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">User</span> &#123;</span><br><span class="line">    String userName;</span><br><span class="line">    <span class="type">int</span> age;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Demo</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        AtomicReference&lt;User&gt; atomicUser = <span class="keyword">new</span> <span class="title class_">AtomicReference</span>&lt;&gt;();</span><br><span class="line">        <span class="type">User</span> <span class="variable">z3</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">User</span>(<span class="string">&quot;z3&quot;</span>, <span class="number">20</span>);</span><br><span class="line">        <span class="type">User</span> <span class="variable">l4</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">User</span>(<span class="string">&quot;l4&quot;</span>, <span class="number">22</span>);</span><br><span class="line">        atomicUser.set(z3);</span><br><span class="line">        System.out.println(atomicUser.compareAndSet(z3, l4) + <span class="string">&quot;\t&quot;</span> + atomicUser.get());</span><br><span class="line">        System.out.println(atomicUser.compareAndSet(z3, l4) + <span class="string">&quot;\t&quot;</span> + atomicUser.get());</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// output</span></span><br><span class="line"><span class="literal">true</span>	<span class="title function_">User</span><span class="params">(userName=l4, age=<span class="number">22</span>)</span></span><br><span class="line"><span class="literal">false</span>	<span class="title function_">User</span><span class="params">(userName=l4, age=<span class="number">22</span>)</span></span><br></pre></td></tr></table></figure>
</li>
<li><p>CAS 与自旋锁（spinlock）</p>
<p><span id="Spinlock"></span></p>
<ul>
<li><p>CAS 是实现自旋锁的基础</p>
</li>
<li><p>自旋：指尝试获取锁的线程不会立即阻塞，而是不断循环尝试获取锁，直到获取</p>
<ul>
<li>优点：减少上下文切换的资源消耗</li>
<li>缺点：CPU 空转，消耗 CPU 资源（ABA 问题，详见下文）</li>
<li>适用场景：每个线程操作耗时短，对锁的占用时间少</li>
</ul>
</li>
<li><p>借助 CAS 手动实现自旋锁</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Demo</span> &#123;</span><br><span class="line">    AtomicReference&lt;Thread&gt; atomicReference = <span class="keyword">new</span> <span class="title class_">AtomicReference</span>&lt;&gt;();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">lock</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="type">Thread</span> <span class="variable">thread</span> <span class="operator">=</span> Thread.currentThread();</span><br><span class="line">        System.out.println(thread.getName() + <span class="string">&quot; come in&quot;</span>);</span><br><span class="line">        <span class="keyword">while</span> (!atomicReference.compareAndSet(<span class="literal">null</span>, thread)) &#123;</span><br><span class="line">            <span class="comment">// 自旋锁</span></span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">unlock</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="type">Thread</span> <span class="variable">thread</span> <span class="operator">=</span> Thread.currentThread();</span><br><span class="line">        atomicReference.compareAndSet(thread, <span class="literal">null</span>);</span><br><span class="line">        System.out.println(thread.getName() + <span class="string">&quot; unlock&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        <span class="type">Demo</span> <span class="variable">demo</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Demo</span>();</span><br><span class="line"></span><br><span class="line">        <span class="keyword">new</span> <span class="title class_">Thread</span>(() -&gt; &#123;</span><br><span class="line">            demo.lock();</span><br><span class="line">            <span class="keyword">try</span> &#123; TimeUnit.SECONDS.sleep(<span class="number">3</span>); &#125; <span class="keyword">catch</span> (InterruptedException e) &#123; e.printStackTrace(); &#125;</span><br><span class="line">            demo.unlock();</span><br><span class="line">        &#125;, <span class="string">&quot;t1&quot;</span>).start();</span><br><span class="line"></span><br><span class="line">        <span class="keyword">try</span> &#123; TimeUnit.SECONDS.sleep(<span class="number">1</span>); &#125; <span class="keyword">catch</span> (InterruptedException e) &#123; e.printStackTrace(); &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">new</span> <span class="title class_">Thread</span>(() -&gt; &#123;</span><br><span class="line">            demo.lock();</span><br><span class="line">            demo.unlock();</span><br><span class="line">        &#125;, <span class="string">&quot;t2&quot;</span>).start();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// output</span></span><br><span class="line">t1 come in</span><br><span class="line">t2 come in</span><br><span class="line">t1 unlock</span><br><span class="line">t2 unlock</span><br></pre></td></tr></table></figure>
</li>
<li><p>ABA 问题</p>
<blockquote>
<p>线程 t1 取出数据 A</p>
<p>线程 t2 取出数据 A 并修改为 B</p>
<p>线程 t2 取出数据 B 并修改为 A</p>
<p>线程 A 未察觉数据被修改过</p>
</blockquote>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">static</span> <span class="type">AtomicInteger</span> <span class="variable">atomicInteger</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">AtomicInteger</span>(<span class="number">5</span>);</span><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">    <span class="keyword">new</span> <span class="title class_">Thread</span>(() -&gt; &#123;</span><br><span class="line">        System.out.println(atomicInteger.compareAndSet(<span class="number">5</span>, <span class="number">10</span>) + <span class="string">&quot;\t&quot;</span> + atomicInteger.get());</span><br><span class="line">        <span class="keyword">try</span> &#123; TimeUnit.MILLISECONDS.sleep(<span class="number">100</span>); &#125; <span class="keyword">catch</span> (InterruptedException e) &#123; e.printStackTrace(); &#125;</span><br><span class="line">        System.out.println(atomicInteger.compareAndSet(<span class="number">10</span>, <span class="number">5</span>) + <span class="string">&quot;\t&quot;</span> + atomicInteger.get());</span><br><span class="line">    &#125;, <span class="string">&quot;t1&quot;</span>).start();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">try</span> &#123; TimeUnit.SECONDS.sleep(<span class="number">1</span>); &#125; <span class="keyword">catch</span> (InterruptedException e) &#123; e.printStackTrace(); &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">new</span> <span class="title class_">Thread</span>(() -&gt; &#123;</span><br><span class="line">        System.out.println(atomicInteger.compareAndSet(<span class="number">5</span>, <span class="number">20</span>) + <span class="string">&quot;\t&quot;</span> + atomicInteger.get());</span><br><span class="line">    &#125;, <span class="string">&quot;t2&quot;</span>).start();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// output</span></span><br><span class="line"><span class="literal">true</span>	<span class="number">10</span></span><br><span class="line"><span class="literal">true</span>	<span class="number">5</span></span><br><span class="line"><span class="literal">true</span>	<span class="number">20</span></span><br></pre></td></tr></table></figure>
</li>
<li><p>AtomicStampedReference 解决 ABA 问题</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">static</span> AtomicStampedReference&lt;Integer&gt; stampedReference = <span class="keyword">new</span> <span class="title class_">AtomicStampedReference</span>(<span class="number">5</span>, <span class="number">1</span>);</span><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">    <span class="keyword">new</span> <span class="title class_">Thread</span>(() -&gt; &#123;</span><br><span class="line">        <span class="type">int</span> <span class="variable">stamp</span> <span class="operator">=</span> stampedReference.getStamp();</span><br><span class="line">        System.out.println(<span class="string">&quot;t1 首次版本号: &quot;</span> + stamp);</span><br><span class="line">        <span class="keyword">try</span> &#123; TimeUnit.SECONDS.sleep(<span class="number">1</span>); &#125; <span class="keyword">catch</span> (InterruptedException e) &#123; e.printStackTrace(); &#125;</span><br><span class="line"></span><br><span class="line">        System.out.println(stampedReference.compareAndSet(<span class="number">5</span>, <span class="number">10</span>, stamp, stamp + <span class="number">1</span>));</span><br><span class="line">        System.out.println(stampedReference.getReference() + <span class="string">&quot;\t&quot;</span> + stampedReference.getStamp());</span><br><span class="line">        <span class="keyword">try</span> &#123; TimeUnit.MILLISECONDS.sleep(<span class="number">100</span>); &#125; <span class="keyword">catch</span> (InterruptedException e) &#123; e.printStackTrace(); &#125;</span><br><span class="line">        System.out.println(stampedReference.compareAndSet(<span class="number">10</span>, <span class="number">5</span>, stamp + <span class="number">1</span>, stamp + <span class="number">2</span>));</span><br><span class="line">        System.out.println(stampedReference.getReference() + <span class="string">&quot;\t&quot;</span> + stampedReference.getStamp());</span><br><span class="line">    &#125;, <span class="string">&quot;t1&quot;</span>).start();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">new</span> <span class="title class_">Thread</span>(() -&gt; &#123;</span><br><span class="line">        <span class="type">int</span> <span class="variable">stamp</span> <span class="operator">=</span> stampedReference.getStamp();</span><br><span class="line">        System.out.println(<span class="string">&quot;t2 首次版本号: &quot;</span> + stamp);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">try</span> &#123; TimeUnit.SECONDS.sleep(<span class="number">3</span>); &#125; <span class="keyword">catch</span> (InterruptedException e) &#123; e.printStackTrace(); &#125;</span><br><span class="line">        System.out.println(stampedReference.compareAndSet(<span class="number">5</span>, <span class="number">20</span>, stamp, stamp + <span class="number">1</span>));</span><br><span class="line">        System.out.println(stampedReference.getReference() + <span class="string">&quot;\t&quot;</span> + stampedReference.getStamp());</span><br><span class="line">    &#125;, <span class="string">&quot;t2&quot;</span>).start();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// output</span></span><br><span class="line">t1 首次版本号: <span class="number">1</span></span><br><span class="line">t2 首次版本号: <span class="number">1</span></span><br><span class="line"><span class="literal">true</span></span><br><span class="line"><span class="number">10</span>	<span class="number">2</span></span><br><span class="line"><span class="literal">true</span></span><br><span class="line"><span class="number">5</span>	<span class="number">3</span></span><br><span class="line"><span class="literal">false</span></span><br><span class="line"><span class="number">5</span>	<span class="number">3</span></span><br></pre></td></tr></table></figure></li>
</ul>
</li>
</ol>
<h2 id="9-原子操作类之-18-罗汉增强"><a href="#9-原子操作类之-18-罗汉增强" class="headerlink" title="9. 原子操作类之 18 罗汉增强"></a>9. 原子操作类之 18 罗汉增强</h2><p><span id="chapter9"></span></p>
<ol>
<li><p><strong>基本类型原子类：</strong></p>
<ul>
<li><p><strong>AtomicBoolean、AtomicInteger、AtomicLong</strong></p>
</li>
<li><p>常见 API</p>
<ul>
<li><code>public final int get()</code>：获取当前值</li>
<li><code>public final int getAndSet(int newValue)</code>：获取当前值，并设置新值</li>
<li><code>public final int getAndIncrement()</code>：获取当前值，并自增</li>
<li><code>public final int getAndDecrement()</code>：获取当前值，并自减</li>
<li><code>public final int getAndAdd(int delta)</code>：获取当前值，并加上预期值</li>
<li><code>public final boolean compareAndSet(int expect, int update)</code>：若输入值等于预期值，则以原子方式将其设为新值（update）</li>
</ul>
</li>
<li><p><strong>CoundDownLatch 使用示例</strong></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">MyNumber</span> &#123;</span><br><span class="line">    <span class="type">AtomicInteger</span> <span class="variable">atomicInteger</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">AtomicInteger</span>();</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">addPlusPlus</span><span class="params">()</span> &#123;</span><br><span class="line">        atomicInteger.getAndIncrement();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Demo</span> &#123;</span><br><span class="line">       <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> InterruptedException &#123;</span><br><span class="line">           <span class="type">int</span> <span class="variable">SIZE</span> <span class="operator">=</span> <span class="number">50</span>;</span><br><span class="line">           <span class="type">MyNumber</span> <span class="variable">myNumber</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">MyNumber</span>();</span><br><span class="line">           <span class="type">CountDownLatch</span> <span class="variable">countDownLatch</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">CountDownLatch</span>(SIZE);</span><br><span class="line">           <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; SIZE; i++) &#123;</span><br><span class="line">               <span class="keyword">new</span> <span class="title class_">Thread</span>(() -&gt; &#123;</span><br><span class="line">                   <span class="keyword">try</span> &#123;</span><br><span class="line">                       <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">j</span> <span class="operator">=</span> <span class="number">0</span>; j &lt; <span class="number">1000</span>; j++) &#123;</span><br><span class="line">                           myNumber.addPlusPlus();</span><br><span class="line">                       &#125;</span><br><span class="line">                   &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">                        countDownLatch.countDown();</span><br><span class="line">                   &#125;</span><br><span class="line">               &#125;, <span class="string">&quot;t&quot;</span> + i).start();</span><br><span class="line">           &#125;</span><br><span class="line"></span><br><span class="line">           <span class="comment">// 用 sleep 方法不确定最终所有线程计算完成时间，sleep 设置多了耗时，设置少了没算完</span></span><br><span class="line"><span class="comment">//           try &#123; TimeUnit.SECONDS.sleep(1); &#125; catch (InterruptedException e) &#123; e.printStackTrace(); &#125;</span></span><br><span class="line">           <span class="comment">// 用 CountDownLatch 代替 sleep</span></span><br><span class="line">           countDownLatch.await();</span><br><span class="line"></span><br><span class="line">           System.out.println(myNumber.atomicInteger.get());</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// output</span></span><br><span class="line"><span class="number">50000</span></span><br></pre></td></tr></table></figure></li>
</ul>
</li>
<li><p><strong>数组类型原子类：</strong></p>
<ul>
<li><p><strong>AtomicIntegerArray、AtomicLongArray、AtomicReferenceArray</strong></p>
</li>
<li><p>类比于 <code>int -&gt; int[]</code></p>
</li>
<li><p>简单示例</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> InterruptedException &#123;</span><br><span class="line">    <span class="type">AtomicIntegerArray</span> <span class="variable">atomicIntegerArray1</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">AtomicIntegerArray</span>(<span class="keyword">new</span> <span class="title class_">int</span>[<span class="number">5</span>]);</span><br><span class="line">    <span class="type">AtomicIntegerArray</span> <span class="variable">atomicIntegerArray2</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">AtomicIntegerArray</span>(<span class="number">5</span>);</span><br><span class="line">    <span class="type">AtomicIntegerArray</span> <span class="variable">atomicIntegerArray3</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">AtomicIntegerArray</span>(<span class="keyword">new</span> <span class="title class_">int</span>[]&#123;<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>, <span class="number">5</span>&#125;);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; <span class="number">5</span>; i++) &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;atomicIntegerArray1: &quot;</span> + atomicIntegerArray1.get(i));</span><br><span class="line">        System.out.println(<span class="string">&quot;atomicIntegerArray2: &quot;</span> + atomicIntegerArray2.get(i));</span><br><span class="line">        System.out.println(<span class="string">&quot;atomicIntegerArray3: &quot;</span> + atomicIntegerArray3.get(i));</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="type">int</span> temp;</span><br><span class="line">    temp = atomicIntegerArray1.getAndSet(<span class="number">2</span>, <span class="number">2022</span>);</span><br><span class="line">    System.out.println(temp + <span class="string">&quot;\t&quot;</span> + atomicIntegerArray1.get(<span class="number">2</span>));</span><br><span class="line"></span><br><span class="line">    temp = atomicIntegerArray1.getAndIncrement(<span class="number">2</span>);</span><br><span class="line">    System.out.println(temp + <span class="string">&quot;\t&quot;</span> + atomicIntegerArray1.get(<span class="number">2</span>));</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// output</span></span><br><span class="line">atomicIntegerArray1: <span class="number">0</span></span><br><span class="line">atomicIntegerArray2: <span class="number">0</span></span><br><span class="line">atomicIntegerArray3: <span class="number">1</span></span><br><span class="line">atomicIntegerArray1: <span class="number">0</span></span><br><span class="line">atomicIntegerArray2: <span class="number">0</span></span><br><span class="line">atomicIntegerArray3: <span class="number">2</span></span><br><span class="line">atomicIntegerArray1: <span class="number">0</span></span><br><span class="line">atomicIntegerArray2: <span class="number">0</span></span><br><span class="line">atomicIntegerArray3: <span class="number">3</span></span><br><span class="line">atomicIntegerArray1: <span class="number">0</span></span><br><span class="line">atomicIntegerArray2: <span class="number">0</span></span><br><span class="line">atomicIntegerArray3: <span class="number">4</span></span><br><span class="line">atomicIntegerArray1: <span class="number">0</span></span><br><span class="line">atomicIntegerArray2: <span class="number">0</span></span><br><span class="line">atomicIntegerArray3: <span class="number">5</span></span><br><span class="line"><span class="number">0</span>	<span class="number">2022</span></span><br><span class="line"><span class="number">2022</span>	<span class="number">2023</span></span><br></pre></td></tr></table></figure></li>
</ul>
</li>
<li><p><strong>引用类型原子类：</strong></p>
<ul>
<li><p><strong>AtomicReference、AtomicStampedReference、AtomicMarkableReference</strong></p>
</li>
<li><p>AtomicReference、AtomicStampedReference 在上一章 CAS 章节中已介绍，不再赘述</p>
</li>
<li><p>AtomicStampedReference </p>
<ul>
<li>用于解决修改过多次的问题（ABA）</li>
<li>状态戳为 Integer 类型</li>
</ul>
</li>
<li><p>AtomicMarkableReference </p>
<ul>
<li>用于解决是否修改过的问题（一次性）</li>
<li>状态戳为 Boolean 类型</li>
</ul>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">static</span> <span class="type">AtomicMarkableReference</span> <span class="variable">markableReference</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">AtomicMarkableReference</span>(<span class="number">100</span>, <span class="literal">false</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> InterruptedException &#123;</span><br><span class="line">    <span class="keyword">new</span> <span class="title class_">Thread</span>(() -&gt; &#123;</span><br><span class="line">        <span class="type">boolean</span> <span class="variable">marked</span> <span class="operator">=</span> markableReference.isMarked();</span><br><span class="line">        System.out.println(<span class="string">&quot;t1 默认标识: &quot;</span> + marked);</span><br><span class="line">        <span class="keyword">try</span> &#123; TimeUnit.SECONDS.sleep(<span class="number">1</span>); &#125; <span class="keyword">catch</span> (InterruptedException e) &#123; e.printStackTrace(); &#125;</span><br><span class="line"></span><br><span class="line">        System.out.println(<span class="string">&quot;t1 &quot;</span> + markableReference.compareAndSet(<span class="number">100</span>, <span class="number">2022</span>, marked, !marked));</span><br><span class="line">        System.out.println(<span class="string">&quot;t1 &quot;</span> + markableReference.getReference());</span><br><span class="line">    &#125;, <span class="string">&quot;t1&quot;</span>).start();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">new</span> <span class="title class_">Thread</span>(() -&gt; &#123;</span><br><span class="line">        <span class="type">boolean</span> <span class="variable">marked</span> <span class="operator">=</span> markableReference.isMarked();</span><br><span class="line">        System.out.println(<span class="string">&quot;t2 默认标识: &quot;</span> + marked);</span><br><span class="line">        <span class="keyword">try</span> &#123; TimeUnit.SECONDS.sleep(<span class="number">1</span>); &#125; <span class="keyword">catch</span> (InterruptedException e) &#123; e.printStackTrace(); &#125;</span><br><span class="line"></span><br><span class="line">        System.out.println(<span class="string">&quot;t2 &quot;</span> + markableReference.compareAndSet(<span class="number">100</span>, <span class="number">2022</span>, marked, !marked));</span><br><span class="line">        System.out.println(<span class="string">&quot;t2 &quot;</span> + markableReference.getReference());</span><br><span class="line">    &#125;, <span class="string">&quot;t2&quot;</span>).start();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// output</span></span><br><span class="line">t1 默认标识: <span class="literal">false</span></span><br><span class="line">t2 默认标识: <span class="literal">false</span></span><br><span class="line">t1 <span class="literal">true</span></span><br><span class="line">t1 <span class="number">2022</span></span><br><span class="line">t2 <span class="literal">false</span></span><br><span class="line">t2 <span class="number">2022</span></span><br></pre></td></tr></table></figure></li>
</ul>
</li>
<li><p><strong>对象的属性修改原子类：</strong></p>
<ul>
<li><p><strong>AtomicIntegerFieldUpdater、AtomicLongFieldUpdater、AtomicReferenceFieldUpdater</strong></p>
</li>
<li><p>使用目的：<strong>以一种线程安全的方式操作非线程安全对象中的某个字段</strong></p>
<blockquote>
<p>在没有该原子类之前，要实现此目的需要对整个对象实例加锁 （synchronized xx方法），效率低下</p>
</blockquote>
</li>
<li><p>使用要求：</p>
<ul>
<li>需要更新的字段必须用 <code>public volatile</code> 修饰</li>
<li>因为<strong>对象的属性修改类型原子类都是抽象类</strong>，所以每次都要使用静态方法<code>newUpdater()</code>创建一个更新器，并设置需要更新的类和属性</li>
</ul>
</li>
<li><p>使用示例：</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">BankAccount</span> &#123;</span><br><span class="line">    <span class="type">String</span> <span class="variable">bankName</span> <span class="operator">=</span> <span class="string">&quot;CCB&quot;</span>;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">volatile</span> <span class="type">int</span> <span class="variable">money</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">    AtomicIntegerFieldUpdater&lt;BankAccount&gt; fieldUpdater =</span><br><span class="line">            AtomicIntegerFieldUpdater.newUpdater(BankAccount.class, <span class="string">&quot;money&quot;</span>);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">add</span><span class="params">(BankAccount bankAccount)</span> &#123;</span><br><span class="line">        fieldUpdater.getAndIncrement(bankAccount);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Demo</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> InterruptedException &#123;</span><br><span class="line">        <span class="type">BankAccount</span> <span class="variable">bankAccount</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">BankAccount</span>();</span><br><span class="line">        <span class="type">CountDownLatch</span> <span class="variable">countDownLatch</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">CountDownLatch</span>(<span class="number">10</span>);</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; <span class="number">10</span>; i++) &#123;</span><br><span class="line">            <span class="keyword">new</span> <span class="title class_">Thread</span>(() -&gt; &#123;</span><br><span class="line">                <span class="keyword">try</span> &#123;</span><br><span class="line">                    <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">j</span> <span class="operator">=</span> <span class="number">0</span>; j &lt; <span class="number">1000</span>; j++) &#123;</span><br><span class="line">                        bankAccount.add(bankAccount);</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">                    countDownLatch.countDown();</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;, <span class="string">&quot;t1&quot;</span>).start();</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        countDownLatch.await();</span><br><span class="line">        System.out.println(<span class="string">&quot;main &quot;</span> + bankAccount.money);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// output</span></span><br><span class="line">main <span class="number">10000</span></span><br></pre></td></tr></table></figure></li>
</ul>
</li>
<li><p><strong>原子操作增强类原理深度解析：</strong></p>
<ul>
<li><p>JDK1.5 版本增加了上述原子类；JDK1.8新增以下四个原子操作增强类</p>
</li>
<li><p>阿里 Java 开发手册，高并发时推荐使用 LongAdder 对象，比 AtomicLong 性能好（低并发差不多）</p>
</li>
<li><p><strong>DoubleAccumulator、DoubleAdder、LongAccumulator、LongAdder</strong></p>
</li>
<li><p>常用 API</p>
<p><img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Java/JUC/%E5%8E%9F%E5%AD%90%E5%A2%9E%E5%BC%BA%E6%93%8D%E4%BD%9C%E7%B1%BB%E5%B8%B8%E7%94%A8API.png" alt="原子增强操作类常用API"></p>
<ul>
<li>LongAdder 只能计算加法，只能从 0 开始</li>
<li>LongAccumulator 可自定义函数操作</li>
</ul>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> InterruptedException &#123;</span><br><span class="line">    <span class="type">LongAdder</span> <span class="variable">longAdder</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">LongAdder</span>();</span><br><span class="line">    longAdder.increment();</span><br><span class="line">    longAdder.increment();</span><br><span class="line">    longAdder.increment();</span><br><span class="line">    System.out.println(longAdder.sum());</span><br><span class="line"></span><br><span class="line">    <span class="type">LongAccumulator</span> <span class="variable">longAccumulator</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">LongAccumulator</span>((x, y) -&gt; x * y, <span class="number">10</span>);</span><br><span class="line">    longAccumulator.accumulate(<span class="number">5</span>);</span><br><span class="line">    longAccumulator.accumulate(<span class="number">10</span>);</span><br><span class="line">    System.out.println(longAccumulator.get());</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// output</span></span><br><span class="line"><span class="number">3</span></span><br><span class="line"><span class="number">500</span></span><br></pre></td></tr></table></figure>
</li>
<li><p>点赞统计示例<strong>（不要求实时精确）</strong></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">ClickNumber</span> &#123;</span><br><span class="line">    <span class="type">int</span> <span class="variable">number</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">synchronized</span> <span class="keyword">void</span> <span class="title function_">clickBySynchronized</span><span class="params">()</span> &#123;</span><br><span class="line">        number++;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="type">AtomicLong</span> <span class="variable">atomicLong</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">AtomicLong</span>(<span class="number">0</span>);</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">clickByAtomicLong</span><span class="params">()</span> &#123;</span><br><span class="line">        atomicLong.getAndIncrement();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="type">LongAdder</span> <span class="variable">longAdder</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">LongAdder</span>();</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">clickByLongAdder</span><span class="params">()</span> &#123;</span><br><span class="line">        longAdder.increment();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="type">LongAccumulator</span> <span class="variable">longAccumulator</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">LongAccumulator</span>((x, y) -&gt; x + y, <span class="number">0</span>);</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">clickByLongAccumulator</span><span class="params">()</span> &#123;</span><br><span class="line">        longAccumulator.accumulate(<span class="number">1</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Demo</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> InterruptedException &#123;</span><br><span class="line">        <span class="type">int</span> <span class="variable">SIZE</span> <span class="operator">=</span> <span class="number">50</span>;</span><br><span class="line">        <span class="type">int</span> <span class="variable">TIMES</span> <span class="operator">=</span> <span class="number">10000</span>;</span><br><span class="line">        <span class="type">ClickNumber</span> <span class="variable">clickNumber</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ClickNumber</span>();</span><br><span class="line">        <span class="type">long</span> startTime;</span><br><span class="line">        <span class="type">long</span> endTime;</span><br><span class="line">        <span class="type">CountDownLatch</span> <span class="variable">countDownLatch1</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">CountDownLatch</span>(SIZE);</span><br><span class="line">        <span class="type">CountDownLatch</span> <span class="variable">countDownLatch2</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">CountDownLatch</span>(SIZE);</span><br><span class="line">        <span class="type">CountDownLatch</span> <span class="variable">countDownLatch3</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">CountDownLatch</span>(SIZE);</span><br><span class="line">        <span class="type">CountDownLatch</span> <span class="variable">countDownLatch4</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">CountDownLatch</span>(SIZE);</span><br><span class="line"></span><br><span class="line">        startTime = System.currentTimeMillis();</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; SIZE; i++) &#123;</span><br><span class="line">            <span class="keyword">new</span> <span class="title class_">Thread</span>(() -&gt; &#123;</span><br><span class="line">                <span class="keyword">try</span> &#123;</span><br><span class="line">                    <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">j</span> <span class="operator">=</span> <span class="number">0</span>; j &lt; <span class="number">100</span> * TIMES; j++) &#123;</span><br><span class="line">                        clickNumber.clickBySynchronized();</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">                    countDownLatch1.countDown();</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;, <span class="string">&quot;t1&quot;</span>).start();</span><br><span class="line">        &#125;</span><br><span class="line">        countDownLatch1.await();</span><br><span class="line">        endTime = System.currentTimeMillis();</span><br><span class="line">        System.out.println(<span class="string">&quot;clickBySynchronized constTime: &quot;</span> + (endTime - startTime) + <span class="string">&quot;毫秒 \t&quot;</span></span><br><span class="line">            + clickNumber.number);</span><br><span class="line"></span><br><span class="line">        startTime = System.currentTimeMillis();</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; SIZE; i++) &#123;</span><br><span class="line">            <span class="keyword">new</span> <span class="title class_">Thread</span>(() -&gt; &#123;</span><br><span class="line">                <span class="keyword">try</span> &#123;</span><br><span class="line">                    <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">j</span> <span class="operator">=</span> <span class="number">0</span>; j &lt; <span class="number">100</span> * TIMES; j++) &#123;</span><br><span class="line">                        clickNumber.clickByAtomicLong();</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">                    countDownLatch2.countDown();</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;, <span class="string">&quot;t2&quot;</span>).start();</span><br><span class="line">        &#125;</span><br><span class="line">        countDownLatch2.await();</span><br><span class="line">        endTime = System.currentTimeMillis();</span><br><span class="line">        System.out.println(<span class="string">&quot;clickByAtomicLong constTime: &quot;</span> + (endTime - startTime) + <span class="string">&quot;毫秒 \t&quot;</span></span><br><span class="line">            + clickNumber.atomicLong.get());</span><br><span class="line"></span><br><span class="line">        startTime = System.currentTimeMillis();</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; SIZE; i++) &#123;</span><br><span class="line">            <span class="keyword">new</span> <span class="title class_">Thread</span>(() -&gt; &#123;</span><br><span class="line">                <span class="keyword">try</span> &#123;</span><br><span class="line">                    <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">j</span> <span class="operator">=</span> <span class="number">0</span>; j &lt; <span class="number">100</span> * TIMES; j++) &#123;</span><br><span class="line">                        clickNumber.clickByLongAdder();</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">                    countDownLatch3.countDown();</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;, <span class="string">&quot;t3&quot;</span>).start();</span><br><span class="line">        &#125;</span><br><span class="line">        countDownLatch3.await();</span><br><span class="line">        endTime = System.currentTimeMillis();</span><br><span class="line">        System.out.println(<span class="string">&quot;clickByLongAdder constTime: &quot;</span> + (endTime - startTime) + <span class="string">&quot;毫秒 \t&quot;</span></span><br><span class="line">            + clickNumber.longAdder.longValue());</span><br><span class="line"></span><br><span class="line">        startTime = System.currentTimeMillis();</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; SIZE; i++) &#123;</span><br><span class="line">            <span class="keyword">new</span> <span class="title class_">Thread</span>(() -&gt; &#123;</span><br><span class="line">                <span class="keyword">try</span> &#123;</span><br><span class="line">                    <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">j</span> <span class="operator">=</span> <span class="number">0</span>; j &lt; <span class="number">100</span> * TIMES; j++) &#123;</span><br><span class="line">                        clickNumber.clickByLongAccumulator();</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">                    countDownLatch4.countDown();</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;, <span class="string">&quot;t4&quot;</span>).start();</span><br><span class="line">        &#125;</span><br><span class="line">        countDownLatch4.await();</span><br><span class="line">        endTime = System.currentTimeMillis();</span><br><span class="line">        System.out.println(<span class="string">&quot;clickByLongAccumulator constTime: &quot;</span> + (endTime - startTime) + <span class="string">&quot;毫秒 \t&quot;</span></span><br><span class="line">            + clickNumber.longAccumulator.get());</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// output</span></span><br><span class="line">clickBySynchronized constTime: <span class="number">1983</span>毫秒 	<span class="number">50000000</span></span><br><span class="line">clickByAtomicLong constTime: <span class="number">984</span>毫秒 	<span class="number">50000000</span></span><br><span class="line">clickByLongAdder constTime: <span class="number">353</span>毫秒 	<span class="number">50000000</span></span><br><span class="line">clickByLongAccumulator constTime: <span class="number">260</span>毫秒 	<span class="number">50000000</span></span><br></pre></td></tr></table></figure>
</li>
<li><p>LongAdder 为什么效率高</p>
<ul>
<li>LongAdder 是 Striped64 的子类</li>
<li>Striped64 的两个重要属性：<ul>
<li><code>transient volatile long base</code>：低并发，直接累加到该变量</li>
<li><code>transient volatile Cell[] cells</code>：高并发，累加进各个线程自己的 Cell[i] 中</li>
</ul>
</li>
<li>Cell 是Striped64 的一个内部类</li>
<li><strong>相比于 AtomicLong 化整为零，LongAdder 用 cell 分散热点</strong></li>
</ul>
<p><img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Java/JUC/LongAdder%E4%B8%BA%E4%BB%80%E4%B9%88%E5%BF%AB.png" alt="LongAdder为什么快"></p>
<p><img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Java/JUC/LongAdder%E7%A4%BA%E6%84%8F%E5%9B%BE.png" alt="LongAdder示意图"></p>
</li>
<li><p><strong><a target="_blank" rel="noopener" href="https://www.bilibili.com/video/BV1ar4y1x727?p=92">底层原理和源码分析</a></strong></p>
<ul>
<li><p><code>add()</code> 源码</p>
<blockquote>
<ol>
<li>最初无竞争只更新 base</li>
<li>更新 base 失败后，首次新建一个 Cell[] （长度为 2）</li>
<li>当多个线程竞争同一个 Cell 比较激烈时，可能就要对 Cell[] 扩容（容量 * 2，为 2 的次幂）</li>
</ol>
</blockquote>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">add</span><span class="params">(<span class="type">long</span> x)</span> &#123;</span><br><span class="line">    Cell[] as; <span class="type">long</span> b, v; <span class="type">int</span> m; Cell a;</span><br><span class="line">    <span class="keyword">if</span> ((as = cells) != <span class="literal">null</span> || !casBase(b = base, b + x)) &#123;</span><br><span class="line">        <span class="type">boolean</span> <span class="variable">uncontended</span> <span class="operator">=</span> <span class="literal">true</span>;</span><br><span class="line">        <span class="keyword">if</span> (as == <span class="literal">null</span> || (m = as.length - <span class="number">1</span>) &lt; <span class="number">0</span> ||</span><br><span class="line">            (a = as[getProbe() &amp; m]) == <span class="literal">null</span> ||</span><br><span class="line">            !(uncontended = a.cas(v = a.value, v + x)))</span><br><span class="line">            longAccumulate(x, <span class="literal">null</span>, uncontended);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Java/JUC/LongAdder%E4%B8%ADadd%E6%96%B9%E6%B3%95%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90.png" alt="LongAdder中add方法源码分析"></p>
</li>
<li><p><code>longAccumulate()</code>源码</p>
<ul>
<li><p>前置知识</p>
<p><img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Java/JUC/Striped64%E4%B8%AD%E7%9A%84%E4%B8%80%E4%BA%9B%E5%8F%98%E9%87%8F%E5%92%8C%E6%96%B9%E6%B3%95.png" alt="Striped64中的一些变量和方法"></p>
</li>
<li><p>线程 hash 值：<code>probe</code></p>
<p><img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Java/JUC/longAccumulate1.png" alt="longAccumulate1"></p>
</li>
<li><p><code>for(;;)</code> 自旋</p>
<p><img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Java/JUC/longAccumulate2.png" alt="longAccumulate2"></p>
</li>
<li><p><strong>CASE2：刚刚要初始化 Cell[]（即首次新建）</strong></p>
<p><img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Java/JUC/longAccumulate2.1.png" alt="longAccumulate2.1"></p>
</li>
<li><p><strong>CASE3：兜底，Cell[] 正在初始化</strong></p>
<p><img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Java/JUC/longAccumulate2.2.png" alt="longAccumulate2.2"></p>
</li>
<li><p><strong>CASE1：Cell[] 不为空且可能需要扩容（最繁琐）</strong></p>
<p>总流程图</p>
<p><img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Java/JUC/longAccumulate2%E6%80%BB%E7%BB%93.png" alt="longAccumulate2总结"></p>
<ol>
<li><p>创建 Cell</p>
<p><img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Java/JUC/longAccumulate2.3.1.png" alt="longAccumulate2.3.1"></p>
</li>
<li><p>判断竞争锁状态，失败则重新竞争</p>
<p> <img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Java/JUC/longAccumulate2.3.2.png" alt="longAccumulate2.3.2"></p>
</li>
<li><p>CAS 更新 cell 数据</p>
<p><img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Java/JUC/longAccumulate2.3.3.png" alt="longAccumulate2.3.3"></p>
</li>
<li><p>数组扩容后大小与 CPU 核数比较</p>
<p> <img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Java/JUC/longAccumulate2.3.4.png" alt="longAccumulate2.3.4"></p>
</li>
<li><p>数组扩容后大小小于 CPU 核数，即可扩容</p>
<p> <img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Java/JUC/longAccumulate2.3.5.png" alt="longAccumulate2.3.5"></p>
</li>
<li><p>扩容为原容量 * 2</p>
<p><img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Java/JUC/longAccumulate2.3.6.png" alt="longAccumulate2.3.6"></p>
</li>
</ol>
</li>
<li><p>代码总览如下</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">// long x 指需要增加的值，一般默认为 1</span></span><br><span class="line"><span class="comment">// LongBinaryOperator fn 默认为 null</span></span><br><span class="line"><span class="comment">// boolean wasUncontended 指竞争标识，false 表示有竞争，只有 cells 初始化之后，并且当前线程 CAS 修改竞争失败才会是 false</span></span><br><span class="line"><span class="keyword">final</span> <span class="keyword">void</span> <span class="title function_">longAccumulate</span><span class="params">(<span class="type">long</span> x, LongBinaryOperator fn,</span></span><br><span class="line"><span class="params">                              <span class="type">boolean</span> wasUncontended)</span> &#123;</span><br><span class="line">    <span class="type">int</span> h;</span><br><span class="line">    <span class="keyword">if</span> ((h = getProbe()) == <span class="number">0</span>) &#123;</span><br><span class="line">        ThreadLocalRandom.current(); <span class="comment">// force initialization</span></span><br><span class="line">        h = getProbe();</span><br><span class="line">        wasUncontended = <span class="literal">true</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="type">boolean</span> <span class="variable">collide</span> <span class="operator">=</span> <span class="literal">false</span>;                <span class="comment">// True if last slot nonempty</span></span><br><span class="line">    <span class="keyword">for</span> (;;) &#123;</span><br><span class="line">        Cell[] as; Cell a; <span class="type">int</span> n; <span class="type">long</span> v;</span><br><span class="line">        <span class="keyword">if</span> ((as = cells) != <span class="literal">null</span> &amp;&amp; (n = as.length) &gt; <span class="number">0</span>) &#123;</span><br><span class="line">            <span class="keyword">if</span> ((a = as[(n - <span class="number">1</span>) &amp; h]) == <span class="literal">null</span>) &#123;</span><br><span class="line">                <span class="keyword">if</span> (cellsBusy == <span class="number">0</span>) &#123;       <span class="comment">// Try to attach new Cell</span></span><br><span class="line">                    <span class="type">Cell</span> <span class="variable">r</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Cell</span>(x);   <span class="comment">// Optimistically create</span></span><br><span class="line">                    <span class="keyword">if</span> (cellsBusy == <span class="number">0</span> &amp;&amp; casCellsBusy()) &#123;</span><br><span class="line">                        <span class="type">boolean</span> <span class="variable">created</span> <span class="operator">=</span> <span class="literal">false</span>;</span><br><span class="line">                        <span class="keyword">try</span> &#123;               <span class="comment">// Recheck under lock</span></span><br><span class="line">                            Cell[] rs; <span class="type">int</span> m, j;</span><br><span class="line">                            <span class="keyword">if</span> ((rs = cells) != <span class="literal">null</span> &amp;&amp;</span><br><span class="line">                                (m = rs.length) &gt; <span class="number">0</span> &amp;&amp;</span><br><span class="line">                                rs[j = (m - <span class="number">1</span>) &amp; h] == <span class="literal">null</span>) &#123;</span><br><span class="line">                                rs[j] = r;</span><br><span class="line">                                created = <span class="literal">true</span>;</span><br><span class="line">                            &#125;</span><br><span class="line">                        &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">                            cellsBusy = <span class="number">0</span>;</span><br><span class="line">                        &#125;</span><br><span class="line">                        <span class="keyword">if</span> (created)</span><br><span class="line">                            <span class="keyword">break</span>;</span><br><span class="line">                        <span class="keyword">continue</span>;           <span class="comment">// Slot is now non-empty</span></span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;</span><br><span class="line">                collide = <span class="literal">false</span>;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">else</span> <span class="keyword">if</span> (!wasUncontended)       <span class="comment">// CAS already known to fail</span></span><br><span class="line">                wasUncontended = <span class="literal">true</span>;      <span class="comment">// Continue after rehash</span></span><br><span class="line">            <span class="keyword">else</span> <span class="keyword">if</span> (a.cas(v = a.value, ((fn == <span class="literal">null</span>) ? v + x :</span><br><span class="line">                                         fn.applyAsLong(v, x))))</span><br><span class="line">                <span class="keyword">break</span>;</span><br><span class="line">            <span class="keyword">else</span> <span class="keyword">if</span> (n &gt;= NCPU || cells != as)</span><br><span class="line">                collide = <span class="literal">false</span>;            <span class="comment">// At max size or stale</span></span><br><span class="line">            <span class="keyword">else</span> <span class="keyword">if</span> (!collide)</span><br><span class="line">                collide = <span class="literal">true</span>;</span><br><span class="line">            <span class="keyword">else</span> <span class="keyword">if</span> (cellsBusy == <span class="number">0</span> &amp;&amp; casCellsBusy()) &#123;</span><br><span class="line">                <span class="keyword">try</span> &#123;</span><br><span class="line">                    <span class="keyword">if</span> (cells == as) &#123;      <span class="comment">// Expand table unless stale</span></span><br><span class="line">                        Cell[] rs = <span class="keyword">new</span> <span class="title class_">Cell</span>[n &lt;&lt; <span class="number">1</span>];</span><br><span class="line">                        <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; n; ++i)</span><br><span class="line">                            rs[i] = as[i];</span><br><span class="line">                        cells = rs;</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">                    cellsBusy = <span class="number">0</span>;</span><br><span class="line">                &#125;</span><br><span class="line">                collide = <span class="literal">false</span>;</span><br><span class="line">                <span class="keyword">continue</span>;                   <span class="comment">// Retry with expanded table</span></span><br><span class="line">            &#125;</span><br><span class="line">            h = advanceProbe(h);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">else</span> <span class="keyword">if</span> (cellsBusy == <span class="number">0</span> &amp;&amp; cells == as &amp;&amp; casCellsBusy()) &#123;</span><br><span class="line">            <span class="type">boolean</span> <span class="variable">init</span> <span class="operator">=</span> <span class="literal">false</span>;</span><br><span class="line">            <span class="keyword">try</span> &#123;                           <span class="comment">// Initialize table</span></span><br><span class="line">                <span class="keyword">if</span> (cells == as) &#123;</span><br><span class="line">                    Cell[] rs = <span class="keyword">new</span> <span class="title class_">Cell</span>[<span class="number">2</span>];</span><br><span class="line">                    rs[h &amp; <span class="number">1</span>] = <span class="keyword">new</span> <span class="title class_">Cell</span>(x);</span><br><span class="line">                    cells = rs;</span><br><span class="line">                    init = <span class="literal">true</span>;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">                cellsBusy = <span class="number">0</span>;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">if</span> (init)</span><br><span class="line">                <span class="keyword">break</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">else</span> <span class="keyword">if</span> (casBase(v = base, ((fn == <span class="literal">null</span>) ? v + x :</span><br><span class="line">                                    fn.applyAsLong(v, x))))</span><br><span class="line">            <span class="keyword">break</span>;                          <span class="comment">// Fall back on using base</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li>
</ul>
</li>
<li><p><code>sum()</code> 源码</p>
<ul>
<li><p>将所有 Cell 数组中的 value 和 base累加作为返回值</p>
<p>核心思想是将 AtomicLong 中一个 value 的更新压力分散到多个 cell 中</p>
</li>
<li><p><code>sum()</code> 执行时并没有限制对 base 和 cells 的更新，所以 <strong>LongAdder 不是强一致性的，而是最终一致性的</strong>（非实时更新，但最终总和是正确的）</p>
</li>
<li><p>代码总览</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="type">long</span> <span class="title function_">sum</span><span class="params">()</span> &#123;</span><br><span class="line">    Cell[] as = cells; Cell a;</span><br><span class="line">    <span class="type">long</span> <span class="variable">sum</span> <span class="operator">=</span> base;</span><br><span class="line">    <span class="keyword">if</span> (as != <span class="literal">null</span>) &#123;</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; as.length; ++i) &#123;</span><br><span class="line">            <span class="keyword">if</span> ((a = as[i]) != <span class="literal">null</span>)</span><br><span class="line">                sum += a.value;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> sum;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li>
</ul>
</li>
</ul>
</li>
<li><p>LongAdder 总结</p>
<ul>
<li>原理：CAS + Base +Cell[]，空间换时间。分散热点</li>
<li>适用场景：高并发下的全局计算（对实时精度要求不高）</li>
<li>缺点：<code>sum()</code> 计算过程中还有线程修改结果会导致当前结果不精确，但能保证最终一致性</li>
</ul>
</li>
</ul>
</li>
</ol>
<h2 id="10-聊聊-ThreadLocal"><a href="#10-聊聊-ThreadLocal" class="headerlink" title="10. 聊聊 ThreadLocal"></a>10. 聊聊 ThreadLocal</h2><ol>
<li><p>大厂面试题</p>
<ul>
<li><p>ThreadLocal 中 ThreadLocalMap 的数据结构和关系</p>
<blockquote>
<p>ThreadLocalMap 是 ThreadLocal 的一个<strong>静态内部类</strong></p>
<p>ThreadLocalMap 本质是一个以 ThreadLocal 实例为 key，任意对象为 value 的 Entry 对象</p>
</blockquote>
</li>
<li><p>ThreadLocal 的 key 为什么是弱引用</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">T1</span> &#123;</span><br><span class="line">    <span class="keyword">volatile</span> <span class="type">boolean</span> flag;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        ThreadLocal&lt;String&gt; tl = <span class="keyword">new</span> <span class="title class_">ThreadLocal</span>&lt;&gt;();</span><br><span class="line">        t1.set(<span class="string">&quot;zcm&quot;</span>);</span><br><span class="line">        t1.get();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Java/JUC/%E4%B8%BA%E4%BB%80%E4%B9%88%E7%94%A8%E5%BC%B1%E5%BC%95%E7%94%A8.png" alt="为什么用弱引用"></p>
</li>
<li><p>ThreadLocal 的内存泄漏问题</p>
<blockquote>
<p>解决方法，清除脏 Entry（<a href="#DirtyEntry">详见本章最后</a>）</p>
</blockquote>
<p><img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Java/JUC/ThreadLocal%E5%86%85%E5%AD%98%E6%B3%84%E6%BC%8F.png" alt="ThreadLocal内存泄漏"></p>
</li>
<li><p>ThreadLocal 中最后为什么要加 remove 方法</p>
<blockquote>
<p>在线程池场景下，线程经常被复用，如不用 remove 方法回收自定义的 ThreadLocal 变量，可能回影响后续业务逻辑和造成内存泄漏等问题</p>
<p>通常在 <code>try-finally</code> 中进行 remove 回收</p>
</blockquote>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Data</span> &#123;</span><br><span class="line">    ThreadLocal&lt;Integer&gt; val = ThreadLocal.withInitial(() -&gt; <span class="number">0</span>);</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">add</span><span class="params">()</span> &#123;</span><br><span class="line">        val.set(val.get() + <span class="number">1</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Demo</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> InterruptedException &#123;</span><br><span class="line">        <span class="type">Data</span> <span class="variable">data</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Data</span>();</span><br><span class="line">        <span class="type">ExecutorService</span> <span class="variable">threadPool</span> <span class="operator">=</span> Executors.newFixedThreadPool(<span class="number">3</span>);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; <span class="number">10</span>; i++) &#123;</span><br><span class="line">                threadPool.submit(() -&gt; &#123;</span><br><span class="line">                    <span class="keyword">try</span> &#123;</span><br><span class="line">                        <span class="type">Integer</span> <span class="variable">before</span> <span class="operator">=</span> data.val.get();</span><br><span class="line">                        data.add();</span><br><span class="line">                        <span class="type">Integer</span> <span class="variable">after</span> <span class="operator">=</span> data.val.get();</span><br><span class="line">                        System.out.println(Thread.currentThread().getName() + <span class="string">&quot; before = &quot;</span> + before + <span class="string">&quot;\t after = &quot;</span> + after);</span><br><span class="line">                    &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">                        <span class="comment">// 删除 remove 有安全隐患</span></span><br><span class="line">                        data.val.remove();</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">        &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">            threadPool.shutdown();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// output 进行 remove</span></span><br><span class="line">pool-<span class="number">1</span>-thread-<span class="number">2</span> before = <span class="number">0</span>	 after = <span class="number">1</span></span><br><span class="line">pool-<span class="number">1</span>-thread-<span class="number">1</span> before = <span class="number">0</span>	 after = <span class="number">1</span></span><br><span class="line">pool-<span class="number">1</span>-thread-<span class="number">2</span> before = <span class="number">0</span>	 after = <span class="number">1</span></span><br><span class="line">pool-<span class="number">1</span>-thread-<span class="number">1</span> before = <span class="number">0</span>	 after = <span class="number">1</span></span><br><span class="line">pool-<span class="number">1</span>-thread-<span class="number">2</span> before = <span class="number">0</span>	 after = <span class="number">1</span></span><br><span class="line">pool-<span class="number">1</span>-thread-<span class="number">2</span> before = <span class="number">0</span>	 after = <span class="number">1</span></span><br><span class="line">pool-<span class="number">1</span>-thread-<span class="number">1</span> before = <span class="number">0</span>	 after = <span class="number">1</span></span><br><span class="line">pool-<span class="number">1</span>-thread-<span class="number">2</span> before = <span class="number">0</span>	 after = <span class="number">1</span></span><br><span class="line">pool-<span class="number">1</span>-thread-<span class="number">1</span> before = <span class="number">0</span>	 after = <span class="number">1</span></span><br><span class="line">pool-<span class="number">1</span>-thread-<span class="number">3</span> before = <span class="number">0</span>	 after = <span class="number">1</span></span><br><span class="line">    </span><br><span class="line"><span class="comment">// output 不进行 remove</span></span><br><span class="line">pool-<span class="number">1</span>-thread-<span class="number">2</span> before = <span class="number">0</span>	 after = <span class="number">1</span></span><br><span class="line">pool-<span class="number">1</span>-thread-<span class="number">3</span> before = <span class="number">0</span>	 after = <span class="number">1</span></span><br><span class="line">pool-<span class="number">1</span>-thread-<span class="number">1</span> before = <span class="number">0</span>	 after = <span class="number">1</span></span><br><span class="line">pool-<span class="number">1</span>-thread-<span class="number">3</span> before = <span class="number">1</span>	 after = <span class="number">2</span></span><br><span class="line">pool-<span class="number">1</span>-thread-<span class="number">2</span> before = <span class="number">1</span>	 after = <span class="number">2</span></span><br><span class="line">pool-<span class="number">1</span>-thread-<span class="number">2</span> before = <span class="number">2</span>	 after = <span class="number">3</span></span><br><span class="line">pool-<span class="number">1</span>-thread-<span class="number">2</span> before = <span class="number">3</span>	 after = <span class="number">4</span></span><br><span class="line">pool-<span class="number">1</span>-thread-<span class="number">2</span> before = <span class="number">4</span>	 after = <span class="number">5</span></span><br><span class="line">pool-<span class="number">1</span>-thread-<span class="number">3</span> before = <span class="number">2</span>	 after = <span class="number">3</span></span><br><span class="line">pool-<span class="number">1</span>-thread-<span class="number">1</span> before = <span class="number">1</span>	 after = <span class="number">2</span></span><br></pre></td></tr></table></figure></li>
</ul>
</li>
<li><p>ThreadLocal 是什么</p>
<blockquote>
<p>ThreadLocal 提供<strong>线程局部变量</strong>，每个线程在访问该变量（getter、setter）时都有自己的、独立初始化的变量版本</p>
<p>ThreadLocal 示例通常是类中的<strong>私有静态字段</strong>，使用目的是希望<strong>将状态和线程关联起来</strong></p>
</blockquote>
</li>
<li><p>ThreadLocal 作用：<strong>群雄逐鹿起纷争 &#x3D;&#x3D;&gt; 人手一份天下安</strong></p>
<p>每个线程都有自己专属的本地变量副本，从而避免共享访问的线程安全问题</p>
</li>
<li><p>API 介绍</p>
<ul>
<li><p>初始化方法</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">// JDK1.8 之前的初始化方法: 匿名内部类</span></span><br><span class="line"><span class="comment">//    ThreadLocal&lt;Integer&gt; saleCount = new ThreadLocal&lt;Integer&gt;()&#123;</span></span><br><span class="line"><span class="comment">//        @Override</span></span><br><span class="line"><span class="comment">//        protected Integer initialValue() &#123;</span></span><br><span class="line"><span class="comment">//            return 0;</span></span><br><span class="line"><span class="comment">//        &#125;</span></span><br><span class="line"><span class="comment">//    &#125;;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// JDK1.8 后的初始化方法（推荐使用）</span></span><br><span class="line">ThreadLocal&lt;Integer&gt; saleCount = ThreadLocal.withInitial(() -&gt; <span class="number">0</span>);</span><br></pre></td></tr></table></figure></li>
</ul>
<p><img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Java/JUC/ThreadLocalAPI.png" alt="ThreadLocalAPI"></p>
</li>
<li><p>源码分析</p>
<blockquote>
<p>Thread、ThreadGroup、ThreadLocal<T>、ThreadLocalMap</p>
<p><img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Java/JUC/ThreadLocal%E4%B8%89%E8%80%85%E5%85%B3%E7%B3%BB.png" alt="ThreadLocal三者关系"></p>
<p>ThreadLocalMap 本质是一个以 ThreadLocal 实例为 key，任意对象为 value 的 Entry 对象</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">void</span> <span class="title function_">createMap</span><span class="params">(Thread t, T firstValue)</span> &#123;</span><br><span class="line">    t.threadLocals = <span class="keyword">new</span> <span class="title class_">ThreadLocalMap</span>(<span class="built_in">this</span>, firstValue);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
</blockquote>
<ul>
<li><p><code>get()</code> 方法</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> T <span class="title function_">get</span><span class="params">()</span> &#123;</span><br><span class="line">    <span class="type">Thread</span> <span class="variable">t</span> <span class="operator">=</span> Thread.currentThread();</span><br><span class="line">    <span class="type">ThreadLocalMap</span> <span class="variable">map</span> <span class="operator">=</span> getMap(t);</span><br><span class="line">    <span class="keyword">if</span> (map != <span class="literal">null</span>) &#123;</span><br><span class="line">        ThreadLocalMap.<span class="type">Entry</span> <span class="variable">e</span> <span class="operator">=</span> map.getEntry(<span class="built_in">this</span>);</span><br><span class="line">        <span class="keyword">if</span> (e != <span class="literal">null</span>) &#123;</span><br><span class="line">            <span class="meta">@SuppressWarnings(&quot;unchecked&quot;)</span></span><br><span class="line">            <span class="type">T</span> <span class="variable">result</span> <span class="operator">=</span> (T)e.value;</span><br><span class="line">            <span class="keyword">return</span> result;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> setInitialValue();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
</li>
<li><p><code>set()</code>方法</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">set</span><span class="params">(T value)</span> &#123;</span><br><span class="line">    <span class="type">Thread</span> <span class="variable">t</span> <span class="operator">=</span> Thread.currentThread();</span><br><span class="line">    <span class="type">ThreadLocalMap</span> <span class="variable">map</span> <span class="operator">=</span> getMap(t);</span><br><span class="line">    <span class="keyword">if</span> (map != <span class="literal">null</span>)</span><br><span class="line">        map.set(<span class="built_in">this</span>, value);</span><br><span class="line">    <span class="keyword">else</span></span><br><span class="line">        createMap(t, value);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li>
</ul>
<p><img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Java/JUC/ThreadLocal%E7%9A%84get&set.png" alt="ThreadLocal的get&amp;set"></p>
</li>
<li><p>强软弱虚四大引用</p>
<p> <img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Java/JUC/%E5%BC%BA%E8%BD%AF%E5%BC%B1%E8%99%9A%E5%9B%9B%E5%A4%A7%E5%BC%95%E7%94%A8.png" alt="强软弱虚四大引用"></p>
<ul>
<li><p><code>finalize()</code> 方法：对象被回收时调用的方法，一般不重写（以下为方便演示案例重写）</p>
</li>
<li><p>强引用（宁愿 OOM 也不会回收）</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">MyObject</span> &#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">protected</span> <span class="keyword">void</span> <span class="title function_">finalize</span><span class="params">()</span> <span class="keyword">throws</span> Throwable &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;finalize方法被调用&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Demo</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> InterruptedException &#123;</span><br><span class="line">        <span class="type">MyObject</span> <span class="variable">myObject</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">MyObject</span>();</span><br><span class="line">        System.out.println(<span class="string">&quot;before gc &quot;</span> + myObject);</span><br><span class="line">        myObject = <span class="literal">null</span>;</span><br><span class="line">        System.gc();</span><br><span class="line">        System.out.println(<span class="string">&quot;before gc &quot;</span> + myObject);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// output</span></span><br><span class="line">before gc MyObject@74a14482</span><br><span class="line">before gc <span class="literal">null</span></span><br><span class="line">finalize方法被调用</span><br></pre></td></tr></table></figure>
</li>
<li><p>软引用（内存不够时回收）</p>
<blockquote>
<p>适用于图片、网页缓存</p>
</blockquote>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">MyObject</span> &#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">protected</span> <span class="keyword">void</span> <span class="title function_">finalize</span><span class="params">()</span> <span class="keyword">throws</span> Throwable &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;finalize方法被调用&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Demo</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> InterruptedException &#123;</span><br><span class="line">        SoftReference&lt;MyObject&gt; softReference = <span class="keyword">new</span> <span class="title class_">SoftReference</span>&lt;&gt;(<span class="keyword">new</span> <span class="title class_">MyObject</span>());</span><br><span class="line">        System.out.println(<span class="string">&quot;before gc &quot;</span> + softReference.get());</span><br><span class="line">        System.gc();</span><br><span class="line">        System.out.println(<span class="string">&quot;after gc &quot;</span> + softReference.get());</span><br><span class="line">        System.out.println(<span class="string">&quot;内存够用，不会被回收&quot;</span>);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="comment">// 20MB &gt; 内存 10MB</span></span><br><span class="line">            <span class="comment">// 设置内存 -Xms10m -Xmx10m</span></span><br><span class="line">            <span class="type">byte</span>[] bytes = <span class="keyword">new</span> <span class="title class_">byte</span>[<span class="number">20</span> * <span class="number">1024</span> * <span class="number">1024</span>];</span><br><span class="line">        &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">        &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">            System.out.println(<span class="string">&quot;内存不够用 &quot;</span> + softReference.get());</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// output</span></span><br><span class="line">before gc MyObject@74a14482</span><br><span class="line">after gc MyObject@74a14482</span><br><span class="line">内存够用，不会被回收</span><br><span class="line">finalize方法被调用</span><br><span class="line">内存不够用 <span class="literal">null</span></span><br><span class="line">Exception in thread <span class="string">&quot;main&quot;</span> java.lang.OutOfMemoryError: Java heap space</span><br><span class="line">	at Demo.main(Demo.java:<span class="number">28</span>)</span><br></pre></td></tr></table></figure>
</li>
<li><p>弱引用（只要进行 gc 就会被回收）</p>
<blockquote>
<p>适用于解决内存泄漏问题</p>
</blockquote>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">MyObject</span> &#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">protected</span> <span class="keyword">void</span> <span class="title function_">finalize</span><span class="params">()</span> <span class="keyword">throws</span> Throwable &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;finalize方法被调用&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Demo</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> InterruptedException &#123;</span><br><span class="line">        WeakReference&lt;MyObject&gt; weakReference = <span class="keyword">new</span> <span class="title class_">WeakReference</span>&lt;&gt;(<span class="keyword">new</span> <span class="title class_">MyObject</span>());</span><br><span class="line">        System.out.println(<span class="string">&quot;before gc &quot;</span> + weakReference.get());</span><br><span class="line">        System.gc();</span><br><span class="line">        System.out.println(<span class="string">&quot;after gc &quot;</span> + weakReference.get());</span><br><span class="line">        System.out.println(<span class="string">&quot;不管内存是否够用，都会被回收&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// output</span></span><br><span class="line">before gc MyObject@74a14482</span><br><span class="line">after gc <span class="literal">null</span></span><br><span class="line">finalize方法被调用</span><br><span class="line">不管内存是否够用，都会被回收</span><br></pre></td></tr></table></figure>
</li>
<li><p>虚引用</p>
<p><img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Java/JUC/%E8%99%9A%E5%BC%95%E7%94%A8.png" alt="虚引用"></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">MyObject</span> &#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">protected</span> <span class="keyword">void</span> <span class="title function_">finalize</span><span class="params">()</span> <span class="keyword">throws</span> Throwable &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;finalize方法被调用&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Demo</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> InterruptedException &#123;</span><br><span class="line">        <span class="type">MyObject</span> <span class="variable">myObject</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">MyObject</span>();</span><br><span class="line">        ReferenceQueue&lt;MyObject&gt; referenceQueue = <span class="keyword">new</span> <span class="title class_">ReferenceQueue</span>&lt;&gt;();</span><br><span class="line">        PhantomReference&lt;MyObject&gt; phantomReference = <span class="keyword">new</span> <span class="title class_">PhantomReference</span>&lt;&gt;(myObject, referenceQueue);</span><br><span class="line">        System.out.println(<span class="string">&quot;虚引用get返回null before gc &quot;</span> + phantomReference.get());</span><br><span class="line"></span><br><span class="line">        List&lt;<span class="type">byte</span>[]&gt; list = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;();</span><br><span class="line">        <span class="keyword">new</span> <span class="title class_">Thread</span>(() -&gt; &#123;</span><br><span class="line">            <span class="keyword">while</span> (<span class="literal">true</span>) &#123;</span><br><span class="line">                list.add(<span class="keyword">new</span> <span class="title class_">byte</span>[<span class="number">1</span> * <span class="number">1024</span> * <span class="number">1024</span>]);</span><br><span class="line">                <span class="keyword">try</span> &#123; TimeUnit.SECONDS.sleep(<span class="number">1</span>); &#125; <span class="keyword">catch</span> (InterruptedException e) &#123; e.printStackTrace(); &#125;</span><br><span class="line">                System.out.println(<span class="string">&quot;add 1MB data to list &quot;</span> + phantomReference.get());</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;, <span class="string">&quot;t1&quot;</span>).start();</span><br><span class="line"></span><br><span class="line">        <span class="keyword">new</span> <span class="title class_">Thread</span>(() -&gt; &#123;</span><br><span class="line">            <span class="keyword">while</span> (<span class="literal">true</span>) &#123;</span><br><span class="line">                Reference&lt;? <span class="keyword">extends</span> <span class="title class_">MyObject</span>&gt; reference = referenceQueue.poll();</span><br><span class="line">                <span class="keyword">if</span>(reference != <span class="literal">null</span>) &#123;</span><br><span class="line">                    System.out.println(<span class="string">&quot;有虚对象回收加入了队列&quot;</span>);</span><br><span class="line">                    <span class="keyword">break</span>;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;, <span class="string">&quot;t2&quot;</span>).start();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// output</span></span><br><span class="line">虚引用get返回<span class="literal">null</span> before gc <span class="literal">null</span></span><br><span class="line">add 1MB data to list <span class="literal">null</span></span><br><span class="line">add 1MB data to list <span class="literal">null</span></span><br><span class="line">add 1MB data to list <span class="literal">null</span></span><br><span class="line">add 1MB data to list <span class="literal">null</span></span><br><span class="line">add 1MB data to list <span class="literal">null</span></span><br><span class="line">add 1MB data to list <span class="literal">null</span></span><br><span class="line">add 1MB data to list <span class="literal">null</span></span><br><span class="line">Exception in thread <span class="string">&quot;t1&quot;</span> java.lang.OutOfMemoryError: Java heap space</span><br><span class="line">	at Demo.lambda$main$<span class="number">0</span>(Demo.java:<span class="number">30</span>)</span><br><span class="line">	at Demo$$Lambda$<span class="number">1</span>/<span class="number">1096979270.</span>run(Unknown Source)</span><br><span class="line">	at java.lang.Thread.run(Thread.java:<span class="number">748</span>)</span><br><span class="line">finalize方法被调用</span><br></pre></td></tr></table></figure></li>
</ul>
</li>
<li><p>清除脏 Entry </p>
<p><span id="DirtyEntry"></span></p>
<ul>
<li><p>造成脏 Entry 原因</p>
<p><img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Java/JUC/%E8%84%8FEntry.png" alt="脏Entry"></p>
</li>
<li><p>解决方法：<code>expungeStaleEntry()</code></p>
<p><code>get()、set()、remove()</code> 方法遇到 key &#x3D;&#x3D; null 的 Entry 时都会调用 <code>expungeStaleEntry()</code> 方法将 value 也置为 null</p>
</li>
</ul>
</li>
<li><p><strong>ThreadLocal 使用的注意事项</strong></p>
<ul>
<li><strong>ThreadLocal.withInitial(() -&gt; 初始化值)</strong></li>
<li><strong>建议用 static 修饰 ThreadLocal</strong> </li>
<li><strong>用完 ThreadLocal 变量记得手动 remove</strong></li>
</ul>
<blockquote>
<p>ThreadLocal 并不解决线程间共享数据的问题</p>
<p>ThreadLocal 适用于变量在线程间隔离但在方法中共享的场景</p>
</blockquote>
</li>
</ol>
<h2 id="11-Java-对象内存布局和对象头"><a href="#11-Java-对象内存布局和对象头" class="headerlink" title="11. Java 对象内存布局和对象头"></a>11. Java 对象内存布局和对象头</h2><ol>
<li><p>对象存储位置：一般在堆内存中的新生代中的伊甸园区</p>
<blockquote>
<p>JVM 内存空间</p>
<p>栈</p>
<p>堆</p>
<ul>
<li>新生代<ul>
<li>伊甸园</li>
<li>survivor0</li>
<li>survivor1</li>
</ul>
</li>
<li>老年代</li>
<li>永久区</li>
</ul>
<p>方法区</p>
</blockquote>
</li>
<li><p>在 HotSpot 虚拟机中，对象在堆内存中的存储布局划分为三个部分</p>
<ul>
<li><p>对象头（Header）：长度 16 字节（不考虑压缩指针）</p>
<ul>
<li><p>对象标记（Mark Word 或 markOop）：长度 8 字节</p>
<ul>
<li>hashCode</li>
<li>GC 标记</li>
<li>GC 次数</li>
<li>同步锁标记</li>
<li>偏向锁持有者</li>
</ul>
<blockquote>
<p>上述信息都是<strong>与对象自身定义无关的数据</strong>，所以 MarkWord 被设计成一个<strong>非固定的数据结构</strong>以便在极小的空间内存储尽量多的数据</p>
<p>MarkWord 会根据对象的状态复用自己的存储空间，即<strong>在运行期间 MarkWord 里存储的数据会随着锁标志位的变化而变化</strong></p>
</blockquote>
<p> <img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Java/JUC/MarkWord%E5%AD%98%E5%82%A8%E7%BB%93%E6%9E%84.png" alt="MarkWord存储结构"></p>
<p><img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Java/JUC/%E5%AF%B9%E8%B1%A1%E5%A4%B4Mark%20Word.png" alt="对象头Mark Word"></p>
</li>
<li><p>类元信息（klassOop 或类型指针 Class Pointer）：长度 8 字节（不考虑压缩指针）</p>
<blockquote>
<p>指向存在方法区的类模板信息</p>
</blockquote>
<p><img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Java/JUC/%E7%B1%BB%E5%9E%8B%E6%8C%87%E9%92%88.png" alt="类型指针"></p>
</li>
</ul>
</li>
<li><p>实例数据（Instance Data）：长度不固定</p>
<blockquote>
<p>存放类的字段（Field）数据信息，包括父类的属性信息</p>
</blockquote>
</li>
<li><p>对齐填充（Padding）：长度不固定</p>
<blockquote>
<p>保证整个对象长度为 8 字节的倍数</p>
</blockquote>
</li>
</ul>
<p>数组对象在对象头中多了个 <code>Length</code>，即数组长度</p>
<p> <img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Java/JUC/%E5%86%85%E5%AD%98%E5%B8%83%E5%B1%80.png" alt="内存布局"></p>
</li>
<li><p>MarkWord 源码详解（64 位操作系统）</p>
<blockquote>
<p>oop.hpp、markOop.hpp</p>
<p>对象分代年龄占 4 位，最大年龄为 15</p>
</blockquote>
<p><img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Java/JUC/markword%E8%AF%A6%E8%A7%A3.png" alt="markword详解"></p>
</li>
<li><p>JOL证明：Java ObjectLayout</p>
<blockquote>
<p>图形化展示 Object 布局</p>
</blockquote>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line">&lt;dependency&gt;</span><br><span class="line">    &lt;groupId&gt;org.openjdk.jol&lt;/groupId&gt;</span><br><span class="line">    &lt;artigactId&gt;jol-core&lt;/artigactId&gt;</span><br><span class="line">    &lt;version&gt;<span class="number">0.9</span>&lt;/version&gt;</span><br><span class="line">&lt;/dependency&gt;</span><br></pre></td></tr></table></figure>

<ul>
<li><p>指针压缩：类型指针 4 字节</p>
<p><img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Java/JUC/%E5%8E%8B%E7%BC%A9%E6%8C%87%E9%92%88Object.png" alt="压缩指针Object"></p>
</li>
<li><p>取消指针压缩：类型指针 8 字节</p>
<p><img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Java/JUC/%E6%97%A0%E5%8E%8B%E7%BC%A9%E6%8C%87%E9%92%88Object.png" alt="无压缩指针Object"></p>
</li>
<li><p>指针压缩参数（默认开启指针压缩）</p>
<blockquote>
<p>展示所有编译参数信息：-XX:+PrintCommandLineFlags -version</p>
<p>默认开启指针压缩参数：-XX:+UserCompressedClassPointers</p>
<p>关闭指针压缩参数：-XX:-UserCompressedClassPointers</p>
</blockquote>
</li>
</ul>
</li>
</ol>
<h2 id="12-Synchronized-与锁升级"><a href="#12-Synchronized-与锁升级" class="headerlink" title="12. Synchronized 与锁升级"></a>12. Synchronized 与锁升级</h2><p><span id="chapter12"></span></p>
<ol>
<li><p>锁升级</p>
<blockquote>
<p>无锁 - 偏向锁 - 轻量级锁 - 重量级锁</p>
<p>锁升级依赖于 MarkWord 中<strong>锁标志位</strong>和<strong>偏向锁标志位</strong></p>
<p><img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Java/JUC/markword%E4%B8%8E%E9%94%81%E5%8D%87%E7%BA%A7.png" alt="markword与锁升级"></p>
</blockquote>
<ul>
<li><p>锁升级的背景</p>
<p>Java5 及之前只有 synchronized（重量级锁），涉及到<strong>内核态和用户态的切换</strong>，资源消耗大</p>
<p>Java6 及之后，为了减少获得锁和释放锁带来的性能消耗，引入了偏向锁和轻量级锁</p>
</li>
<li><p>所有 Object 都可以作为锁</p>
<p><img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Java/JUC/Object%E9%83%BD%E5%8F%AF%E4%BB%A5%E6%88%90%E4%B8%BA%E9%94%81.png" alt="Object都可以成为锁"></p>
</li>
<li><p>流程图</p>
<p><span id="synchronized流程3"></span></p>
<p><img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Java/JUC/synchronized%E6%B5%81%E7%A8%8B3.png" alt="synchronized流程3"></p>
</li>
</ul>
</li>
<li><p>多线程访问情况</p>
<ul>
<li>只有一个线程访问——偏向锁</li>
<li>两个线程交替访问——轻量级锁</li>
<li>竞争激烈，多个线程访问——重量级锁</li>
</ul>
</li>
<li><p>大小端存储复习</p>
<blockquote>
<p>存储一个 32 bit 整数 0x12345678</p>
<p>字节间顺序不同，但是字节内顺序一致</p>
</blockquote>
<table>
<thead>
<tr>
<th align="center">存储模式\地址偏移</th>
<th align="center">0x00</th>
<th align="center">0x01</th>
<th align="center">0x02</th>
<th align="center">0x03</th>
</tr>
</thead>
<tbody><tr>
<td align="center">大端模式</td>
<td align="center">12</td>
<td align="center">34</td>
<td align="center">56</td>
<td align="center">78</td>
</tr>
<tr>
<td align="center">小端模式</td>
<td align="center">78</td>
<td align="center">56</td>
<td align="center">34</td>
<td align="center">12</td>
</tr>
</tbody></table>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">    <span class="keyword">if</span> (ByteOrder.nativeOrder() == ByteOrder.BIG_ENDIAN) &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;BIG_ENDIAN&quot;</span>);</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;LITTLE_ENDIAN&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// output</span></span><br><span class="line">LITTLE_ENDIAN</span><br></pre></td></tr></table></figure>
</li>
<li><p><a target="_blank" rel="noopener" href="https://www.bilibili.com/video/BV1ar4y1x727?p=124">无锁详解</a></p>
<ul>
<li><p>MarkWord 存储 hashcode（如果有调用），若无调用则记录 0（相当于 unused）</p>
<p><img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Java/JUC/%E6%97%A0%E9%94%81%E8%AF%A6%E8%A7%A3.png" alt="无锁详解"></p>
</li>
</ul>
</li>
<li><p>偏向锁详解</p>
<ul>
<li><p>MarkWord 存储偏向线程的 ID</p>
<p><img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Java/JUC/%E5%81%8F%E5%90%91%E9%94%81%E8%AF%A6%E8%A7%A3.png" alt="偏向锁详解"></p>
</li>
<li><p>当一段同步代码一直被同一个线程多次访问，那么该线程在后续访问时自动获得锁（提高性能）</p>
</li>
<li><p>类比老顾客点菜（老样子）</p>
</li>
<li><p><strong>偏向锁会偏向于当前持有锁的线程</strong>，如没有其他线程请求获得该锁，则持有偏向锁的线程永远不需要触发同步，相当于<strong>在没有资源竞争的情况下消除了同步语句，省略 CAS 操作</strong></p>
</li>
<li><p>操作流程——理论落地</p>
<p><img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Java/JUC/%E5%81%8F%E5%90%91%E9%94%81%E7%90%86%E8%AE%BA%E8%90%BD%E5%9C%B0.png" alt="偏向锁理论落地"></p>
</li>
<li><p>操作流程——技术实现</p>
<p><img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Java/JUC/%E5%81%8F%E5%90%91%E9%94%81%E6%8A%80%E6%9C%AF%E5%AE%9E%E7%8E%B0.png" alt="偏向锁技术实现"></p>
</li>
<li><p>偏向锁开启时默认有 4 秒延迟</p>
<blockquote>
<p>开启偏向锁：-XX:+UserBiasedLocking </p>
<p>设置开启延迟为 0 ms：-XX:+BiasedLockingStartupDelay&#x3D;0</p>
<p>关闭偏向锁：-XX:-UserBiasedLocking （直接由无锁升级到轻量级锁）</p>
</blockquote>
</li>
<li><p>偏向锁的撤销 </p>
<blockquote>
<p>有其他线程逐步来竞争锁时，竞争线程尝试 CAS 更新对象头失败，等到<strong>全局安全点（此时不会执行任何代码）</strong>时撤销偏向锁，升级为轻量级锁</p>
</blockquote>
<p><img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Java/JUC/%E5%81%8F%E5%90%91%E9%94%81%E6%92%A4%E9%94%80%E8%AF%B4%E6%98%8E.png" alt="偏向锁撤销说明"></p>
<p><img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Java/JUC/%E5%81%8F%E5%90%91%E9%94%81%E7%9A%84%E6%92%A4%E9%94%80.png" alt="偏向锁的撤销"></p>
</li>
<li><p>Java15 后逐步废弃偏向锁</p>
</li>
</ul>
</li>
<li><p>轻量级锁详解</p>
<ul>
<li><p>MarkWord 存储指向线程栈重 Lock Record 的指针</p>
<p><img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Java/JUC/%E8%BD%BB%E9%87%8F%E7%BA%A7%E9%94%81%E8%AF%A6%E8%A7%A3.png" alt="轻量级锁详解"></p>
</li>
<li><p>多线程竞争，但是任意时刻最多只有一个线程竞争，即<strong>竞争不激烈，无线程阻塞</strong>（用 CAS 替代阻塞）</p>
</li>
<li><p><strong>在线程近乎交替执行时提高性能</strong></p>
</li>
<li><p>流程说明</p>
<p><img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Java/JUC/%E8%BD%BB%E9%87%8F%E9%94%81%E6%96%87%E5%AD%97%E8%AF%B4%E6%98%8E.png" alt="轻量锁文字说明"></p>
</li>
<li><p><strong>CAS 达到一定次数</strong>时升级为重量级锁</p>
<ul>
<li>Java6 前：默认自选次数 10 次 &#x2F; 自选次数超过 CPU 个数一半</li>
<li>Java6 及之后：<strong>自适应自旋锁</strong>——如自旋成功，则下次最大自旋次数增加；如自旋失败，则下次最大自旋次数减少甚至不自旋，避免 CPU 空转</li>
</ul>
</li>
</ul>
</li>
<li><p>重量级锁详解</p>
<ul>
<li><p>MarkWord 存储指向堆中 monitor 对象的指针</p>
<p><img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Java/JUC/%E9%87%8D%E9%87%8F%E7%BA%A7%E9%94%81%E8%AF%A6%E8%A7%A3.png" alt="重量级锁详解"></p>
</li>
<li><p>有大量线程参与竞争，冲突激烈</p>
</li>
<li><p>重量级锁原理</p>
<p><img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Java/JUC/%E9%87%8D%E9%87%8F%E7%BA%A7%E9%94%81%E5%8E%9F%E7%90%86%E8%AF%B4%E6%98%8E.png" alt="重量级锁原理说明"></p>
</li>
</ul>
</li>
<li><p><strong>整体锁流程图</strong></p>
<p><img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Java/JUC/%E9%94%81%E6%95%B4%E4%BD%93%E6%B5%81%E7%A8%8B.jpg" alt="锁整体流程"></p>
</li>
<li><p>锁升级后 hashcode 保存在什么位置</p>
<blockquote>
<p>锁升级为轻、重量级后，没有位置再存放 hashcode、GC 标识、GC 年龄等</p>
<p><strong>当一个对象已经计算过 hashcode 后，就再也无法进入偏向锁状态；如在处于偏向锁状态时计算 hashcode，则偏向锁立即撤销并升级为重量级锁</strong></p>
</blockquote>
<p><img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Java/JUC/%E9%94%81%E4%B8%8Ehashcode%E5%85%B1%E5%AD%98%E9%97%AE%E9%A2%98.png" alt="锁与hashcode共存问题"></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 偏向锁 -&gt; 计算 hashCode -&gt; 升级为轻量锁</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">    <span class="comment">// 等待偏向锁开启</span></span><br><span class="line">    <span class="keyword">try</span> &#123; TimeUnit.SECONDS.sleep(<span class="number">1</span>); &#125; <span class="keyword">catch</span> (InterruptedException e) &#123; e.printStackTrace(); &#125;</span><br><span class="line">    <span class="type">Object</span> <span class="variable">o</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Object</span>();</span><br><span class="line">    System.out.println(<span class="string">&quot;本应是偏向锁 &quot;</span> + ClassLayout.parseInstance(<span class="number">0</span>).toPirntable());</span><br><span class="line"></span><br><span class="line">    o.hashCode();</span><br><span class="line">    <span class="keyword">synchronized</span> (o) &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;计算过 hashcode 后升级为轻量级锁&quot;</span>);</span><br><span class="line">        System.out.println(ClassLayout.parseInstance(<span class="number">0</span>).toPirntable());</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 偏向锁过程中计算 hashCode -&gt; 升级为重量锁</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">    <span class="comment">// 等待偏向锁开启</span></span><br><span class="line">    <span class="keyword">try</span> &#123; TimeUnit.SECONDS.sleep(<span class="number">1</span>); &#125; <span class="keyword">catch</span> (InterruptedException e) &#123; e.printStackTrace(); &#125;</span><br><span class="line">    <span class="type">Object</span> <span class="variable">o</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Object</span>();</span><br><span class="line">    <span class="keyword">synchronized</span> (o) &#123;</span><br><span class="line">        o.hashCode();</span><br><span class="line">        System.out.println(<span class="string">&quot;偏向锁过程中计算 hashcode ，立即撤销偏向锁，升级为重量级锁&quot;</span>);</span><br><span class="line">        System.out.println(ClassLayout.parseInstance(<span class="number">0</span>).toPirntable());</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
</li>
<li><p>对比总结</p>
<p><img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Java/JUC/%E9%94%81%E6%80%BB%E7%BB%93%E5%AF%B9%E6%AF%94.png" alt="锁总结对比"></p>
</li>
<li><p>JIT（Just In Time Compiler） 编译器对锁的优化</p>
<ul>
<li><p>锁消除</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Demo</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">m1</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="comment">// 锁的不是同一个对象，没有必要加锁</span></span><br><span class="line">        <span class="comment">// 底层会将 synchronized 消除</span></span><br><span class="line">        <span class="type">Object</span> <span class="variable">objectLock</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Object</span>();</span><br><span class="line">        <span class="keyword">synchronized</span> (objectLock) &#123;</span><br><span class="line">            System.out.println(<span class="string">&quot;hello objectLock&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        <span class="type">Demo</span> <span class="variable">demo</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Demo</span>();</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; <span class="number">10</span>; i++) &#123;</span><br><span class="line">            <span class="keyword">new</span> <span class="title class_">Thread</span>(() -&gt; &#123;</span><br><span class="line">                demo.m1();</span><br><span class="line">            &#125;, <span class="string">&quot;t1&quot;</span>).start();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
</li>
<li><p>锁粗化</p>
<blockquote>
<p>如方法中首尾相接、前后相邻都是同一个锁对象，则 JIT 编译器会将这些同步代码块合并</p>
<p>避免多次申请和释放锁，提升了性能</p>
</blockquote>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">    <span class="type">Object</span> <span class="variable">objectLock</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Object</span>();</span><br><span class="line">    <span class="keyword">new</span> <span class="title class_">Thread</span>(() -&gt; &#123;</span><br><span class="line">        <span class="keyword">synchronized</span> (objectLock) &#123;</span><br><span class="line">            System.out.println(<span class="string">&quot;111&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">synchronized</span> (objectLock) &#123;</span><br><span class="line">            System.out.println(<span class="string">&quot;222&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">synchronized</span> (objectLock) &#123;</span><br><span class="line">            System.out.println(<span class="string">&quot;333&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;, <span class="string">&quot;t1&quot;</span>).start();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 相当于</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">    <span class="type">Object</span> <span class="variable">objectLock</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Object</span>();</span><br><span class="line">    <span class="keyword">new</span> <span class="title class_">Thread</span>(() -&gt; &#123;</span><br><span class="line">        <span class="keyword">synchronized</span> (objectLock) &#123;</span><br><span class="line">            System.out.println(<span class="string">&quot;111&quot;</span>);</span><br><span class="line">            System.out.println(<span class="string">&quot;222&quot;</span>);</span><br><span class="line">            System.out.println(<span class="string">&quot;333&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;, <span class="string">&quot;t1&quot;</span>).start();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li>
</ul>
</li>
</ol>
<h2 id="13-AbstractQueuedSynchronizer-之-AQS"><a href="#13-AbstractQueuedSynchronizer-之-AQS" class="headerlink" title="13 AbstractQueuedSynchronizer 之 AQS"></a>13 AbstractQueuedSynchronizer 之 AQS</h2><p><span id="chapter13"></span></p>
<ol>
<li><p>AQS：抽象的队列同步器</p>
<blockquote>
<p>位置：<code>java.util.concurrent.AbstractQueueSynchronizer</code></p>
<p>重要性：AQS 之于 JUC，相当于 JVM 之于 Java</p>
<p>是高并发的底层支柱</p>
</blockquote>
</li>
<li><p>前置知识</p>
<ul>
<li>公平锁非公平锁</li>
<li>可重入锁</li>
<li>自旋思想</li>
<li>LockSupport</li>
<li>数据结构之双向链表</li>
<li>设计模式之模板设计模式</li>
</ul>
</li>
<li><p>入门理论知识</p>
<ul>
<li><p>继承关系图</p>
<blockquote>
<p><code>AbstractQueuedSynchronizer</code> 是 jdk1.5 出现的</p>
<p><code>AbstractQueuedLongSynchronizer</code> 是 jdk1.6 出现的</p>
<p>两者为孪生兄弟</p>
</blockquote>
<p><img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Java/JUC/AQS%E7%B1%BB%E5%9B%BE.png" alt="AQS类图"></p>
<blockquote>
<p>AQS 的实现类</p>
</blockquote>
<p> <img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Java/JUC/AQS%E7%9A%84%E5%AE%9E%E7%8E%B0%E7%B1%BB.png" alt="AQS的实现类"></p>
</li>
<li><p>技术原理</p>
<blockquote>
<p>是用来实现锁或者其他同步器组件的<strong>公共基础部分的抽象实现</strong></p>
<p>是<strong>重量级基础框架及整个 JUC 体系的基石</strong></p>
<p>主要用于解决<strong>锁分配给谁</strong>的问题</p>
</blockquote>
</li>
<li><p>CLH 队列（Craig、Landin、Hagersten）</p>
<blockquote>
<p>是一个单向链表，AQS 中的队列是 CLH 变体的虚拟双向队列 FIFO</p>
<p><img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Java/JUC/AQS%E8%99%9A%E6%8B%9F%E5%8F%8C%E7%AB%AF%E9%98%9F%E5%88%97.png" alt="AQS虚拟双端队列"></p>
</blockquote>
</li>
<li><p>为什么说 AQS 是 JUC 的基石</p>
<blockquote>
<p>使用过的同步类 <code>ReentrantLock、CoundDownLatch、ReentrantReadWriteLock、Semaphore</code>等底层都由 AQS 提供支持</p>
<p><img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Java/JUC/AQS%E6%94%AF%E6%8C%81%E6%A1%86%E6%9E%B6.png" alt="AQS支持框架"></p>
<p>锁和同步器关系：锁是使用者，同步器是实现者（ReentrantLock 举例）</p>
<p><img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Java/JUC/AQS%E5%92%8CReentrantLock.png" alt="AQS和ReentrantLock"></p>
</blockquote>
</li>
<li><p>AQS 作用</p>
<blockquote>
<p>加锁会导致阻塞，有阻塞就需要排队，实现排队用队列</p>
<p><img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Java/JUC/AQS%E5%8E%9F%E7%90%86%E8%AF%B4%E6%98%8E.png" alt="AQS原理说明"></p>
<p><img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Java/JUC/AQS%E6%BA%90%E7%A0%81%E7%AE%80%E8%A6%81.png" alt="AQS源码简要"></p>
</blockquote>
</li>
</ul>
</li>
<li><p>源码分析前置知识</p>
<ul>
<li><p>state 和 CLH 队列：实现了排队阻塞等待</p>
<blockquote>
<p>state &#x3D;&#x3D; 0 表示资源空闲； state &#x3D;&#x3D; 1 表示资源被占用</p>
<p>CLH 队列：通过 state 判断是否阻塞，阻塞时<strong>自旋等待</strong>的单向队列</p>
</blockquote>
</li>
<li><p>AQS 自身属性和 Node 节点</p>
<blockquote>
<p>Node 是 AQS 的静态内部类 <code>static final class Node&#123;...&#125;</code></p>
<p>AQS 中的头尾节点 ：</p>
<ul>
<li><code>private transient volatile Node head;</code></li>
<li><code>private transient volatile Node tail;</code></li>
</ul>
<p>Node 节点主要属性：</p>
<ul>
<li><code>static final Node SHARED = new Node()</code>：共享锁</li>
<li><code>static final Node EXCLUSIVE = null</code>：互斥锁</li>
<li><code>static final int CANCELLED = 1</code>：表示线程取消等待（不再排队）</li>
<li><code>static final int SIGNAL = -1</code>：表示后续线程需要被唤醒</li>
<li><code>static final int CONDITION = -2</code>：表示线程等待 condition 唤醒</li>
<li><code>static final int PROPAGATE = -3</code>：表示共享式同步状态获取将会无条件传播下去</li>
<li><code>volatile int waitStatus</code>：描述当前节点的等待状态，初始为 0，状态为以上几种</li>
<li><code>volatile Node prev</code>：前置节点</li>
<li><code>volatile Node next</code>：后置节点</li>
<li><code>volatile Thread thread</code>：对应封装线程</li>
</ul>
<p><img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Java/JUC/Node%E8%8A%82%E7%82%B9%E5%B1%9E%E6%80%A7.png" alt="Node节点属性"></p>
</blockquote>
</li>
</ul>
</li>
<li><p>AQS 源码分析</p>
<blockquote>
<p>Lock 接口的实现类基本都是通过<strong>聚合</strong>一个<strong>队列同步器的子类</strong>完成线程访问控制</p>
<p>源码以 <code>ReentrantLock</code> 为例</p>
<p><img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Java/JUC/AQS%E5%92%8CReentrantLock%E4%BB%A3%E7%A0%81%E5%85%B3%E7%B3%BB.png" alt="AQS和ReentrantLock代码关系"></p>
</blockquote>
<ul>
<li><p><code>lock()</code> 方法</p>
<blockquote>
<p>非公平锁</p>
</blockquote>
<p> <img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Java/JUC/LOCK%E9%9D%9E%E5%85%AC%E5%B9%B3.png" alt="LOCK非公平"></p>
<blockquote>
<p>公平锁</p>
</blockquote>
<p> <img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Java/JUC/LOCK%E5%85%AC%E5%B9%B3.png" alt="LOCK公平"></p>
</li>
<li><p><code>acquire()</code> 方法</p>
<blockquote>
<p>三个分支：<code>tryAcquire、addWaiter、acquireQueued</code></p>
</blockquote>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">final</span> <span class="keyword">void</span> <span class="title function_">acquire</span><span class="params">(<span class="type">int</span> arg)</span> &#123;</span><br><span class="line">    <span class="keyword">if</span> (!tryAcquire(arg) &amp;&amp;</span><br><span class="line">        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))</span><br><span class="line">        selfInterrupt();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Java/JUC/acquire%E4%B8%89%E5%A4%A7%E6%B5%81%E7%A8%8B1.png" alt="acquire三大流程1"></p>
<p><img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Java/JUC/acquire%E4%B8%89%E5%A4%A7%E6%B5%81%E7%A8%8B2.png" alt="acquire三大流程2"></p>
<blockquote>
<p>模拟场景：</p>
<p>A B C 三个顾客去银行办理业务</p>
<p>A 第一个到，优先办理，但是 A 耗时严重，长期占用窗口</p>
<p>B 第二个到，进入 AQS 等待队列，尝试抢占窗口</p>
<p>C 第三个到，进入 AQS 等待队列，尝试抢占窗口，前一个顾客是 B</p>
<p><strong>P148集-AQS之源码分析03开始看（即 tryAcquire() 跳转链接），理解以下三个方法</strong></p>
</blockquote>
</li>
<li><p><a target="_blank" rel="noopener" href="https://www.bilibili.com/video/BV1ar4y1x727?p=148"><code>tryAcquire()</code> 方法</a></p>
<blockquote>
<p>非公平锁为例</p>
</blockquote>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">protected</span> <span class="keyword">final</span> <span class="type">boolean</span> <span class="title function_">tryAcquire</span><span class="params">(<span class="type">int</span> acquires)</span> &#123;</span><br><span class="line">    <span class="keyword">return</span> nonfairTryAcquire(acquires);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">final</span> <span class="type">boolean</span> <span class="title function_">nonfairTryAcquire</span><span class="params">(<span class="type">int</span> acquires)</span> &#123;</span><br><span class="line">    <span class="keyword">final</span> <span class="type">Thread</span> <span class="variable">current</span> <span class="operator">=</span> Thread.currentThread();</span><br><span class="line">    <span class="type">int</span> <span class="variable">c</span> <span class="operator">=</span> getState();</span><br><span class="line">    <span class="keyword">if</span> (c == <span class="number">0</span>) &#123;</span><br><span class="line">        <span class="keyword">if</span> (compareAndSetState(<span class="number">0</span>, acquires)) &#123;</span><br><span class="line">            setExclusiveOwnerThread(current);</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">else</span> <span class="keyword">if</span> (current == getExclusiveOwnerThread()) &#123;</span><br><span class="line">        <span class="type">int</span> <span class="variable">nextc</span> <span class="operator">=</span> c + acquires;</span><br><span class="line">        <span class="keyword">if</span> (nextc &lt; <span class="number">0</span>) <span class="comment">// overflow</span></span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">Error</span>(<span class="string">&quot;Maximum lock count exceeded&quot;</span>);</span><br><span class="line">        setState(nextc);</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
</li>
<li><p><code>addWaiter(Node.EXCLUSIVE)</code> 方法</p>
<blockquote>
<p>非公平锁为例</p>
<p><code>enq(node)</code> 入队，第一个节点为哨兵节点，不存储数据</p>
</blockquote>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">private</span> Node <span class="title function_">addWaiter</span><span class="params">(Node mode)</span> &#123;</span><br><span class="line">    <span class="type">Node</span> <span class="variable">node</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Node</span>(Thread.currentThread(), mode);</span><br><span class="line">    <span class="comment">// Try the fast path of enq; backup to full enq on failure</span></span><br><span class="line">    <span class="type">Node</span> <span class="variable">pred</span> <span class="operator">=</span> tail;</span><br><span class="line">    <span class="keyword">if</span> (pred != <span class="literal">null</span>) &#123;</span><br><span class="line">        node.prev = pred;</span><br><span class="line">        <span class="keyword">if</span> (compareAndSetTail(pred, node)) &#123;</span><br><span class="line">            pred.next = node;</span><br><span class="line">            <span class="keyword">return</span> node;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    enq(node);</span><br><span class="line">    <span class="keyword">return</span> node;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span> Node <span class="title function_">enq</span><span class="params">(<span class="keyword">final</span> Node node)</span> &#123;</span><br><span class="line">    <span class="keyword">for</span> (;;) &#123;</span><br><span class="line">        <span class="type">Node</span> <span class="variable">t</span> <span class="operator">=</span> tail;</span><br><span class="line">        <span class="keyword">if</span> (t == <span class="literal">null</span>) &#123; <span class="comment">// Must initialize</span></span><br><span class="line">            <span class="keyword">if</span> (compareAndSetHead(<span class="keyword">new</span> <span class="title class_">Node</span>()))</span><br><span class="line">                tail = head;</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            node.prev = t;</span><br><span class="line">            <span class="keyword">if</span> (compareAndSetTail(t, node)) &#123;</span><br><span class="line">                t.next = node;</span><br><span class="line">                <span class="keyword">return</span> t;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
</li>
<li><p><code>acquireQueued(addWaiter(Node.EXCLUSIVE), arg)</code> 方法</p>
<blockquote>
<p>非公平锁为例</p>
</blockquote>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">final</span> <span class="type">boolean</span> <span class="title function_">acquireQueued</span><span class="params">(<span class="keyword">final</span> Node node, <span class="type">int</span> arg)</span> &#123;</span><br><span class="line">    <span class="type">boolean</span> <span class="variable">failed</span> <span class="operator">=</span> <span class="literal">true</span>;</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        <span class="type">boolean</span> <span class="variable">interrupted</span> <span class="operator">=</span> <span class="literal">false</span>;</span><br><span class="line">        <span class="keyword">for</span> (;;) &#123;</span><br><span class="line">            <span class="keyword">final</span> <span class="type">Node</span> <span class="variable">p</span> <span class="operator">=</span> node.predecessor();</span><br><span class="line">            <span class="keyword">if</span> (p == head &amp;&amp; tryAcquire(arg)) &#123;</span><br><span class="line">                setHead(node);</span><br><span class="line">                p.next = <span class="literal">null</span>; <span class="comment">// help GC</span></span><br><span class="line">                failed = <span class="literal">false</span>;</span><br><span class="line">                <span class="keyword">return</span> interrupted;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">if</span> (shouldParkAfterFailedAcquire(p, node) &amp;&amp;</span><br><span class="line">                parkAndCheckInterrupt())</span><br><span class="line">                interrupted = <span class="literal">true</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (failed)</span><br><span class="line">            cancelAcquire(node);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">setHead</span><span class="params">(Node node)</span> &#123;</span><br><span class="line">    head = node;</span><br><span class="line">    node.thread = <span class="literal">null</span>;</span><br><span class="line">    node.prev = <span class="literal">null</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="type">boolean</span> <span class="title function_">shouldParkAfterFailedAcquire</span><span class="params">(Node pred, Node node)</span> &#123;</span><br><span class="line">    <span class="type">int</span> <span class="variable">ws</span> <span class="operator">=</span> pred.waitStatus;</span><br><span class="line">    <span class="keyword">if</span> (ws == Node.SIGNAL)</span><br><span class="line">        <span class="comment">/*</span></span><br><span class="line"><span class="comment">         * This node has already set status asking a release</span></span><br><span class="line"><span class="comment">         * to signal it, so it can safely park.</span></span><br><span class="line"><span class="comment">         */</span></span><br><span class="line">        <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">    <span class="keyword">if</span> (ws &gt; <span class="number">0</span>) &#123;</span><br><span class="line">        <span class="comment">/*</span></span><br><span class="line"><span class="comment">         * Predecessor was cancelled. Skip over predecessors and</span></span><br><span class="line"><span class="comment">         * indicate retry.</span></span><br><span class="line"><span class="comment">         */</span></span><br><span class="line">        <span class="keyword">do</span> &#123;</span><br><span class="line">            node.prev = pred = pred.prev;</span><br><span class="line">        &#125; <span class="keyword">while</span> (pred.waitStatus &gt; <span class="number">0</span>);</span><br><span class="line">        pred.next = node;</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="comment">/*</span></span><br><span class="line"><span class="comment">         * waitStatus must be 0 or PROPAGATE.  Indicate that we</span></span><br><span class="line"><span class="comment">         * need a signal, but don&#x27;t park yet.  Caller will need to</span></span><br><span class="line"><span class="comment">         * retry to make sure it cannot acquire before parking.</span></span><br><span class="line"><span class="comment">         */</span></span><br><span class="line">        compareAndSetWaitStatus(pred, ws, Node.SIGNAL);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">final</span> <span class="type">boolean</span> <span class="title function_">parkAndCheckInterrupt</span><span class="params">()</span> &#123;</span><br><span class="line">    LockSupport.park(<span class="built_in">this</span>);</span><br><span class="line">    <span class="keyword">return</span> Thread.interrupted();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">cancelAcquire</span><span class="params">(Node node)</span> &#123;</span><br><span class="line">    <span class="comment">// Ignore if node doesn&#x27;t exist</span></span><br><span class="line">    <span class="keyword">if</span> (node == <span class="literal">null</span>)</span><br><span class="line">        <span class="keyword">return</span>;</span><br><span class="line"></span><br><span class="line">    node.thread = <span class="literal">null</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// Skip cancelled predecessors</span></span><br><span class="line">    <span class="type">Node</span> <span class="variable">pred</span> <span class="operator">=</span> node.prev;</span><br><span class="line">    <span class="keyword">while</span> (pred.waitStatus &gt; <span class="number">0</span>)</span><br><span class="line">        node.prev = pred = pred.prev;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// predNext is the apparent node to unsplice. CASes below will</span></span><br><span class="line">    <span class="comment">// fail if not, in which case, we lost race vs another cancel</span></span><br><span class="line">    <span class="comment">// or signal, so no further action is necessary.</span></span><br><span class="line">    <span class="type">Node</span> <span class="variable">predNext</span> <span class="operator">=</span> pred.next;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// Can use unconditional write instead of CAS here.</span></span><br><span class="line">    <span class="comment">// After this atomic step, other Nodes can skip past us.</span></span><br><span class="line">    <span class="comment">// Before, we are free of interference from other threads.</span></span><br><span class="line">    node.waitStatus = Node.CANCELLED;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// If we are the tail, remove ourselves.</span></span><br><span class="line">    <span class="keyword">if</span> (node == tail &amp;&amp; compareAndSetTail(node, pred)) &#123;</span><br><span class="line">        compareAndSetNext(pred, predNext, <span class="literal">null</span>);</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="comment">// If successor needs signal, try to set pred&#x27;s next-link</span></span><br><span class="line">        <span class="comment">// so it will get one. Otherwise wake it up to propagate.</span></span><br><span class="line">        <span class="type">int</span> ws;</span><br><span class="line">        <span class="keyword">if</span> (pred != head &amp;&amp;</span><br><span class="line">            ((ws = pred.waitStatus) == Node.SIGNAL ||</span><br><span class="line">             (ws &lt;= <span class="number">0</span> &amp;&amp; compareAndSetWaitStatus(pred, ws, Node.SIGNAL))) &amp;&amp;</span><br><span class="line">            pred.thread != <span class="literal">null</span>) &#123;</span><br><span class="line">            <span class="type">Node</span> <span class="variable">next</span> <span class="operator">=</span> node.next;</span><br><span class="line">            <span class="keyword">if</span> (next != <span class="literal">null</span> &amp;&amp; next.waitStatus &lt;= <span class="number">0</span>)</span><br><span class="line">                compareAndSetNext(pred, predNext, next);</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            unparkSuccessor(node);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        node.next = node; <span class="comment">// help GC</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
</li>
<li><p><code>unlock()</code> 方法</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">unlock</span><span class="params">()</span> &#123;</span><br><span class="line">    sync.release(<span class="number">1</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">final</span> <span class="type">boolean</span> <span class="title function_">release</span><span class="params">(<span class="type">int</span> arg)</span> &#123;</span><br><span class="line">    <span class="keyword">if</span> (tryRelease(arg)) &#123;</span><br><span class="line">        <span class="type">Node</span> <span class="variable">h</span> <span class="operator">=</span> head;</span><br><span class="line">        <span class="keyword">if</span> (h != <span class="literal">null</span> &amp;&amp; h.waitStatus != <span class="number">0</span>)</span><br><span class="line">            unparkSuccessor(h);</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">protected</span> <span class="keyword">final</span> <span class="type">boolean</span> <span class="title function_">tryRelease</span><span class="params">(<span class="type">int</span> releases)</span> &#123;</span><br><span class="line">    <span class="type">int</span> <span class="variable">c</span> <span class="operator">=</span> getState() - releases;</span><br><span class="line">    <span class="keyword">if</span> (Thread.currentThread() != getExclusiveOwnerThread())</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">IllegalMonitorStateException</span>();</span><br><span class="line">    <span class="type">boolean</span> <span class="variable">free</span> <span class="operator">=</span> <span class="literal">false</span>;</span><br><span class="line">    <span class="keyword">if</span> (c == <span class="number">0</span>) &#123;</span><br><span class="line">        free = <span class="literal">true</span>;</span><br><span class="line">        setExclusiveOwnerThread(<span class="literal">null</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    setState(c);</span><br><span class="line">    <span class="keyword">return</span> free;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">unparkSuccessor</span><span class="params">(Node node)</span> &#123;</span><br><span class="line">    <span class="comment">/*</span></span><br><span class="line"><span class="comment">     * If status is negative (i.e., possibly needing signal) try</span></span><br><span class="line"><span class="comment">     * to clear in anticipation of signalling.  It is OK if this</span></span><br><span class="line"><span class="comment">     * fails or if status is changed by waiting thread.</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="type">int</span> <span class="variable">ws</span> <span class="operator">=</span> node.waitStatus;</span><br><span class="line">    <span class="keyword">if</span> (ws &lt; <span class="number">0</span>)</span><br><span class="line">        compareAndSetWaitStatus(node, ws, <span class="number">0</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">/*</span></span><br><span class="line"><span class="comment">     * Thread to unpark is held in successor, which is normally</span></span><br><span class="line"><span class="comment">     * just the next node.  But if cancelled or apparently null,</span></span><br><span class="line"><span class="comment">     * traverse backwards from tail to find the actual</span></span><br><span class="line"><span class="comment">     * non-cancelled successor.</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="type">Node</span> <span class="variable">s</span> <span class="operator">=</span> node.next;</span><br><span class="line">    <span class="keyword">if</span> (s == <span class="literal">null</span> || s.waitStatus &gt; <span class="number">0</span>) &#123;</span><br><span class="line">        s = <span class="literal">null</span>;</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">Node</span> <span class="variable">t</span> <span class="operator">=</span> tail; t != <span class="literal">null</span> &amp;&amp; t != node; t = t.prev)</span><br><span class="line">            <span class="keyword">if</span> (t.waitStatus &lt;= <span class="number">0</span>)</span><br><span class="line">                s = t;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">if</span> (s != <span class="literal">null</span>)</span><br><span class="line">        LockSupport.unpark(s.thread);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li>
</ul>
</li>
</ol>
<h2 id="14-ReentreantLock、ReentrantReadWriteLock、StampedLock-详解"><a href="#14-ReentreantLock、ReentrantReadWriteLock、StampedLock-详解" class="headerlink" title="14 ReentreantLock、ReentrantReadWriteLock、StampedLock 详解"></a>14 ReentreantLock、ReentrantReadWriteLock、StampedLock 详解</h2><p><span id="chapter14"></span></p>
<ol>
<li><p>读写锁：一个资源能够被多个读线程访问（读读可共享），或者一个写线程访问</p>
</li>
<li><p>无锁—独占锁（synchronized、ReentrantLock）—读写锁（ReentrantReadWriteLock）—邮戳锁（StampedLock）</p>
<ul>
<li><p>无锁：天下大乱</p>
</li>
<li><p>独占锁：读读不共享，有线程在读时不允许其他线程进行写</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">MyResource</span> &#123;</span><br><span class="line">    Map&lt;String, String&gt; map = <span class="keyword">new</span> <span class="title class_">HashMap</span>&lt;&gt;();</span><br><span class="line">    <span class="type">Lock</span> <span class="variable">lock</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ReentrantLock</span>();</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">write</span><span class="params">(String key, String value)</span> &#123;</span><br><span class="line">        lock.lock();</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            System.out.println(Thread.currentThread().getName() + <span class="string">&quot; 正在写入&quot;</span>);</span><br><span class="line">            map.put(key, value);</span><br><span class="line">            <span class="keyword">try</span> &#123; TimeUnit.SECONDS.sleep(<span class="number">1</span>); &#125; <span class="keyword">catch</span> (InterruptedException e) &#123; e.printStackTrace(); &#125;</span><br><span class="line">            System.out.println(Thread.currentThread().getName() + <span class="string">&quot; 完成写入&quot;</span>);</span><br><span class="line">        &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">            lock.unlock();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">read</span><span class="params">(String key)</span> &#123;</span><br><span class="line">        lock.lock();</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            System.out.println(Thread.currentThread().getName() + <span class="string">&quot; 正在读取&quot;</span>);</span><br><span class="line">            <span class="type">String</span> <span class="variable">result</span> <span class="operator">=</span> map.get(key);</span><br><span class="line">            <span class="keyword">try</span> &#123; TimeUnit.MILLISECONDS.sleep(<span class="number">200</span>); &#125; <span class="keyword">catch</span> (InterruptedException e) &#123; e.printStackTrace(); &#125;</span><br><span class="line">            System.out.println(Thread.currentThread().getName() + <span class="string">&quot; 完成读取 &quot;</span> + result);</span><br><span class="line">        &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">            lock.unlock();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Demo</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        <span class="type">MyResource</span> <span class="variable">myResource</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">MyResource</span>();</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; <span class="number">10</span>; i++) &#123;</span><br><span class="line">            <span class="type">int</span> <span class="variable">finalI</span> <span class="operator">=</span> i;</span><br><span class="line">            <span class="keyword">new</span> <span class="title class_">Thread</span>(() -&gt; &#123;</span><br><span class="line">                myResource.write(finalI + <span class="string">&quot;&quot;</span>, finalI + <span class="string">&quot;&quot;</span>);</span><br><span class="line">            &#125;, <span class="string">&quot;t&quot;</span> + i).start();</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; <span class="number">10</span>; i++) &#123;</span><br><span class="line">            <span class="type">int</span> <span class="variable">finalI</span> <span class="operator">=</span> i;</span><br><span class="line">            <span class="keyword">new</span> <span class="title class_">Thread</span>(() -&gt; &#123;</span><br><span class="line">                myResource.read(finalI + <span class="string">&quot;&quot;</span>);</span><br><span class="line">            &#125;, <span class="string">&quot;t&quot;</span> + i).start();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// output</span></span><br><span class="line">t0 正在写入</span><br><span class="line">t0 完成写入</span><br><span class="line">t1 正在写入</span><br><span class="line">t1 完成写入</span><br><span class="line">t2 正在写入</span><br><span class="line">t2 完成写入</span><br><span class="line">t3 正在写入</span><br><span class="line">t3 完成写入</span><br><span class="line">t4 正在写入</span><br><span class="line">t4 完成写入</span><br><span class="line">t0 正在读取</span><br><span class="line">t0 完成读取 <span class="number">0</span></span><br><span class="line">t1 正在读取</span><br><span class="line">t1 完成读取 <span class="number">1</span></span><br><span class="line">t2 正在读取</span><br><span class="line">t2 完成读取 <span class="number">2</span></span><br><span class="line">t3 正在读取</span><br><span class="line">t3 完成读取 <span class="number">3</span></span><br><span class="line">t4 正在读取</span><br><span class="line">t4 完成读取 <span class="number">4</span></span><br></pre></td></tr></table></figure>
</li>
<li><p>读写锁：写饥饿、锁降级问题</p>
<blockquote>
<p>演示读读共享，其与互斥</p>
</blockquote>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">MyResource</span> &#123;</span><br><span class="line">    Map&lt;String, String&gt; map = <span class="keyword">new</span> <span class="title class_">HashMap</span>&lt;&gt;();</span><br><span class="line">    <span class="type">ReadWriteLock</span> <span class="variable">rwLock</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ReentrantReadWriteLock</span>();</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">write</span><span class="params">(String key, String value)</span> &#123;</span><br><span class="line">        rwLock.writeLock().lock();</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            System.out.println(Thread.currentThread().getName() + <span class="string">&quot; 正在写入&quot;</span>);</span><br><span class="line">            map.put(key, value);</span><br><span class="line">            <span class="keyword">try</span> &#123; TimeUnit.SECONDS.sleep(<span class="number">1</span>); &#125; <span class="keyword">catch</span> (InterruptedException e) &#123; e.printStackTrace(); &#125;</span><br><span class="line">            System.out.println(Thread.currentThread().getName() + <span class="string">&quot; 完成写入&quot;</span>);</span><br><span class="line">        &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">            rwLock.writeLock().unlock();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">read</span><span class="params">(String key)</span> &#123;</span><br><span class="line">        rwLock.readLock().lock();</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            System.out.println(Thread.currentThread().getName() + <span class="string">&quot; 正在读取&quot;</span>);</span><br><span class="line">            <span class="type">String</span> <span class="variable">result</span> <span class="operator">=</span> map.get(key);</span><br><span class="line">            <span class="keyword">try</span> &#123; TimeUnit.MILLISECONDS.sleep(<span class="number">200</span>); &#125; <span class="keyword">catch</span> (InterruptedException e) &#123; e.printStackTrace(); &#125;</span><br><span class="line">            System.out.println(Thread.currentThread().getName() + <span class="string">&quot; 完成读取 &quot;</span> + result);</span><br><span class="line">        &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">            rwLock.readLock().unlock();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Demo</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        <span class="type">MyResource</span> <span class="variable">myResource</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">MyResource</span>();</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; <span class="number">3</span>; i++) &#123;</span><br><span class="line">            <span class="type">int</span> <span class="variable">finalI</span> <span class="operator">=</span> i;</span><br><span class="line">            <span class="keyword">new</span> <span class="title class_">Thread</span>(() -&gt; &#123;</span><br><span class="line">                myResource.write(finalI + <span class="string">&quot;&quot;</span>, finalI + <span class="string">&quot;&quot;</span>);</span><br><span class="line">            &#125;, <span class="string">&quot;t&quot;</span> + i).start();</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; <span class="number">3</span>; i++) &#123;</span><br><span class="line">            <span class="type">int</span> <span class="variable">finalI</span> <span class="operator">=</span> i;</span><br><span class="line">            <span class="keyword">new</span> <span class="title class_">Thread</span>(() -&gt; &#123;</span><br><span class="line">                myResource.read(finalI + <span class="string">&quot;&quot;</span>);</span><br><span class="line">            &#125;, <span class="string">&quot;t&quot;</span> + i).start();</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; <span class="number">3</span>; i++) &#123;</span><br><span class="line">            <span class="type">int</span> <span class="variable">finalI</span> <span class="operator">=</span> i + <span class="number">10</span>;</span><br><span class="line">            <span class="keyword">new</span> <span class="title class_">Thread</span>(() -&gt; &#123;</span><br><span class="line">                myResource.write(finalI + <span class="string">&quot;&quot;</span>, finalI + <span class="string">&quot;&quot;</span>);</span><br><span class="line">            &#125;, <span class="string">&quot;t&quot;</span> + i).start();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// output</span></span><br><span class="line">t1 正在写入</span><br><span class="line">t1 完成写入</span><br><span class="line">t0 正在写入</span><br><span class="line">t0 完成写入</span><br><span class="line">t2 正在写入</span><br><span class="line">t2 完成写入</span><br><span class="line">t0 正在读取</span><br><span class="line">t1 正在读取</span><br><span class="line">t2 正在读取</span><br><span class="line">t2 完成读取 <span class="number">2</span></span><br><span class="line">t1 完成读取 <span class="number">1</span></span><br><span class="line">t0 完成读取 <span class="number">0</span></span><br><span class="line">t0 正在写入</span><br><span class="line">t0 完成写入</span><br><span class="line">t1 正在写入</span><br><span class="line">t1 完成写入</span><br><span class="line">t2 正在写入</span><br><span class="line">t2 完成写入</span><br></pre></td></tr></table></figure>

<blockquote>
<p>锁降级演示</p>
<p><img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Java/JUC/%E9%94%81%E9%99%8D%E7%BA%A7.png" alt="锁降级"></p>
<p>只有降级，没有升级</p>
<p>锁降级是为了让当前线程感知到数据的变化，目的是保证数据的可见性</p>
<p><img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Java/JUC/%E9%94%81%E9%99%8D%E7%BA%A72.png" alt="锁降级2"></p>
</blockquote>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">    <span class="type">ReentrantReadWriteLock</span> <span class="variable">readWriteLock</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ReentrantReadWriteLock</span>();</span><br><span class="line">    ReentrantReadWriteLock.<span class="type">WriteLock</span> <span class="variable">writeLock</span> <span class="operator">=</span> readWriteLock.writeLock();</span><br><span class="line">    ReentrantReadWriteLock.<span class="type">ReadLock</span> <span class="variable">readLock</span> <span class="operator">=</span> readWriteLock.readLock();</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 写锁降级为读锁</span></span><br><span class="line">    writeLock.lock();</span><br><span class="line">    System.out.println(<span class="string">&quot;----写入&quot;</span>);</span><br><span class="line">    readLock.lock();</span><br><span class="line">    System.out.println(<span class="string">&quot;----读取&quot;</span>);</span><br><span class="line">    writeLock.unlock();</span><br><span class="line">    readLock.unlock();</span><br><span class="line"></span><br><span class="line">    System.out.println(<span class="string">&quot;===============&quot;</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 读锁不能升级为写锁</span></span><br><span class="line">    readLock.lock();</span><br><span class="line">    System.out.println(<span class="string">&quot;----读取&quot;</span>);</span><br><span class="line">    writeLock.lock();</span><br><span class="line">    System.out.println(<span class="string">&quot;----写入&quot;</span>);</span><br><span class="line">    writeLock.unlock();</span><br><span class="line">    readLock.unlock();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// output</span></span><br><span class="line">----写入</span><br><span class="line">----读取</span><br><span class="line">===============</span><br><span class="line">----读取</span><br><span class="line">......(不能获取写锁，卡死)</span><br></pre></td></tr></table></figure>

<p><img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Java/JUC/%E5%86%99%E9%94%81%E8%AF%BB%E9%94%81%E4%BA%92%E6%96%A5.png" alt="写锁读锁互斥"></p>
<blockquote>
<p>为什么要有锁降级？<strong>为了保证数据的可见性</strong></p>
<p><img src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Java/JUC/%E4%B8%BA%E4%BB%80%E4%B9%88%E6%9C%89%E9%94%81%E9%99%8D%E7%BA%A7.png" alt="为什么有锁降级"></p>
</blockquote>
</li>
<li><p>写锁饥饿问题的两种解决方法（推荐 StampLock）</p>
<ul>
<li><p>使用 <code>公平策略</code> 可以在一定程度解决写饥饿问题</p>
</li>
<li><p>StampLock 采取<strong>乐观获取锁</strong>，即<strong>其他线程尝试获取写锁时不会被阻塞，但是获取乐观读锁后还需要对结果进行校验</strong></p>
</li>
</ul>
</li>
</ul>
</li>
<li><p>邮戳锁（版本锁、票据锁）：<strong>比读写锁更快的锁</strong></p>
<blockquote>
<p>StampedLock 是 JDK1.8 中新增的一个读写锁，是对 <code>ReentrantReadWriteLock</code> 的优化 </p>
</blockquote>
<ul>
<li><p>Stamp（long 类型）</p>
<p>代表锁的状态。当 stamp 返回 0 时表示线程获取锁失败。当释放锁或者转换锁时都需要传入最初获取的 stamp 值</p>
</li>
<li><p>StampLock 的特点</p>
<ul>
<li>所有获取锁的方法都返回一个邮戳 Stamp，Stamp &#x3D;&#x3D; 0 表示获取失败</li>
<li>所有获取锁的方法都需要传入一个邮戳 Stamp，该 Stamp 必须和成功获取锁时得到的 Stamp 一致</li>
<li>StampLock 是不可重入的（如果一个线程已经持有写锁，再获取写锁时会造成死锁）</li>
<li>StampLock 有三种访问模式<ul>
<li>Reading（悲观读模式）：和 ReentrantReadWriteLock 读锁类似</li>
<li>Writing（写模式）：和  ReentrantReadWriteLock 写锁类似</li>
<li>Optimistic reading（乐观读模式）：无锁机制，类似于数据库中的乐观锁，支持<strong>读写并发</strong>（<strong>乐观认为读取时没有人修改，如被修改再升级为悲观读模式</strong>）</li>
</ul>
</li>
<li>主要特点：<strong>读的过程也允许写锁介入</strong></li>
</ul>
</li>
<li><p>StampLock 代码演示</p>
<blockquote>
<p>演示1：悲观读 + 写（与 ReentrantReadWriteLock 类似）</p>
</blockquote>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Demo</span> &#123;</span><br><span class="line">    <span class="keyword">static</span> <span class="type">int</span> <span class="variable">num</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">static</span> <span class="type">StampedLock</span> <span class="variable">stampedLock</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">StampedLock</span>();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">write</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="type">long</span> <span class="variable">stamp</span> <span class="operator">=</span> stampedLock.writeLock();</span><br><span class="line">        System.out.println(Thread.currentThread().getName() + <span class="string">&quot; 写线程准备修改&quot;</span>);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            num += <span class="number">3</span>;</span><br><span class="line">        &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">            stampedLock.unlockWrite(stamp);</span><br><span class="line">        &#125;</span><br><span class="line">        System.out.println(Thread.currentThread().getName() + <span class="string">&quot; 写线程完成修改&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">read</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="type">long</span> <span class="variable">stamp</span> <span class="operator">=</span> stampedLock.readLock();</span><br><span class="line">        System.out.println(Thread.currentThread().getName() + <span class="string">&quot; 读线程 come in&quot;</span>);</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; <span class="number">4</span>; i++) &#123;</span><br><span class="line">            <span class="keyword">try</span> &#123; TimeUnit.SECONDS.sleep(<span class="number">1</span>); &#125; <span class="keyword">catch</span> (InterruptedException e) &#123; e.printStackTrace(); &#125;</span><br><span class="line">            System.out.println(Thread.currentThread().getName() + <span class="string">&quot; 正在读取中......&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="type">int</span> <span class="variable">result</span> <span class="operator">=</span> num;</span><br><span class="line">            System.out.println(Thread.currentThread().getName() + <span class="string">&quot; 获得变量值 &quot;</span> + result);</span><br><span class="line">            System.out.println(<span class="string">&quot;写线程修改失败，读过程中写锁无法介入&quot;</span>);</span><br><span class="line">        &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">            stampedLock.unlockRead(stamp);</span><br><span class="line">        &#125;</span><br><span class="line">        System.out.println(Thread.currentThread().getName() + <span class="string">&quot; 读线程 over&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        <span class="type">Demo</span> <span class="variable">demo</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Demo</span>();</span><br><span class="line">        <span class="keyword">new</span> <span class="title class_">Thread</span>(() -&gt; &#123;</span><br><span class="line">            demo.read();</span><br><span class="line">        &#125;, <span class="string">&quot;readThread&quot;</span>).start();</span><br><span class="line"></span><br><span class="line">        <span class="keyword">try</span> &#123; TimeUnit.SECONDS.sleep(<span class="number">1</span>); &#125; <span class="keyword">catch</span> (InterruptedException e) &#123; e.printStackTrace(); &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">new</span> <span class="title class_">Thread</span>(() -&gt; &#123;</span><br><span class="line">            demo.write();</span><br><span class="line">        &#125;, <span class="string">&quot;writeThread&quot;</span>).start();</span><br><span class="line">        </span><br><span class="line">        </span><br><span class="line">        <span class="keyword">try</span> &#123; TimeUnit.SECONDS.sleep(<span class="number">5</span>); &#125; <span class="keyword">catch</span> (InterruptedException e) &#123; e.printStackTrace(); &#125;</span><br><span class="line">        System.out.println(Thread.currentThread().getName() + <span class="string">&quot; &quot;</span> + num);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// output</span></span><br><span class="line">readThread 读线程 come in</span><br><span class="line">readThread 正在读取中......</span><br><span class="line">readThread 正在读取中......</span><br><span class="line">readThread 正在读取中......</span><br><span class="line">readThread 正在读取中......</span><br><span class="line">readThread 获得变量值 <span class="number">0</span></span><br><span class="line">写线程修改失败，读过程中写锁无法介入</span><br><span class="line">writeThread 写线程准备修改</span><br><span class="line">writeThread 写线程完成修改</span><br><span class="line">readThread 读线程 over</span><br><span class="line">main <span class="number">3</span></span><br></pre></td></tr></table></figure>

<blockquote>
<p>乐观读：读过程中允许写锁介入，但是一旦发现被修改则升级为悲观读</p>
</blockquote>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Demo</span> &#123;</span><br><span class="line">    <span class="keyword">static</span> <span class="type">int</span> <span class="variable">num</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">static</span> <span class="type">StampedLock</span> <span class="variable">stampedLock</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">StampedLock</span>();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">write</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="type">long</span> <span class="variable">stamp</span> <span class="operator">=</span> stampedLock.writeLock();</span><br><span class="line">        System.out.println(Thread.currentThread().getName() + <span class="string">&quot; 写线程准备修改&quot;</span>);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            num += <span class="number">3</span>;</span><br><span class="line">        &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">            stampedLock.unlockWrite(stamp);</span><br><span class="line">        &#125;</span><br><span class="line">        System.out.println(Thread.currentThread().getName() + <span class="string">&quot; 写线程完成修改&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">optimisticRead</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="type">long</span> <span class="variable">stamp</span> <span class="operator">=</span> stampedLock.tryOptimisticRead();</span><br><span class="line">        <span class="type">int</span> <span class="variable">result</span> <span class="operator">=</span> num;</span><br><span class="line">        System.out.println(<span class="string">&quot;4s前 stampedLock.validate 方法值（true无修改，false已被修改）&quot;</span> + stampedLock.validate(stamp));</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; <span class="number">4</span>; i++) &#123;</span><br><span class="line">            <span class="keyword">try</span> &#123; TimeUnit.SECONDS.sleep(<span class="number">1</span>); &#125; <span class="keyword">catch</span> (InterruptedException e) &#123; e.printStackTrace(); &#125;</span><br><span class="line">            System.out.println(Thread.currentThread().getName() + <span class="string">&quot; 正在读取中......&quot;</span> + i +</span><br><span class="line">                    <span class="string">&quot;秒后 stampedLock.validate 方法值 &quot;</span> + stampedLock.validate(stamp));</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span>(!stampedLock.validate(stamp)) &#123;</span><br><span class="line">            System.out.println(<span class="string">&quot;写操作修改过 num 值&quot;</span>);</span><br><span class="line">            stamp = stampedLock.readLock();</span><br><span class="line"></span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                System.out.println(<span class="string">&quot;从乐观读升级为悲观读&quot;</span>);</span><br><span class="line">                result = num;</span><br><span class="line">                System.out.println(<span class="string">&quot;悲观读后 num = &quot;</span> + result);</span><br><span class="line">            &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">                stampedLock.unlockRead(stamp);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        <span class="type">Demo</span> <span class="variable">demo</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Demo</span>();</span><br><span class="line">        <span class="keyword">new</span> <span class="title class_">Thread</span>(() -&gt; &#123;</span><br><span class="line">            demo.optimisticRead();</span><br><span class="line">        &#125;, <span class="string">&quot;readThread&quot;</span>).start();</span><br><span class="line"></span><br><span class="line">        <span class="keyword">try</span> &#123; TimeUnit.SECONDS.sleep(<span class="number">1</span>); &#125; <span class="keyword">catch</span> (InterruptedException e) &#123; e.printStackTrace(); &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">new</span> <span class="title class_">Thread</span>(() -&gt; &#123;</span><br><span class="line">            System.out.println(Thread.currentThread().getName() + <span class="string">&quot; come in&quot;</span>);</span><br><span class="line">            demo.write();</span><br><span class="line">        &#125;, <span class="string">&quot;writeThread&quot;</span>).start();</span><br><span class="line"></span><br><span class="line">        <span class="keyword">try</span> &#123; TimeUnit.SECONDS.sleep(<span class="number">5</span>); &#125; <span class="keyword">catch</span> (InterruptedException e) &#123; e.printStackTrace(); &#125;</span><br><span class="line">        System.out.println(Thread.currentThread().getName() + <span class="string">&quot; &quot;</span> + num);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// output</span></span><br><span class="line">4s前 stampedLock.validate 方法值（<span class="literal">true</span>无修改，<span class="literal">false</span>已被修改）<span class="literal">true</span></span><br><span class="line">readThread 正在读取中.....<span class="number">.0</span>秒后 stampedLock.validate 方法值 <span class="literal">true</span></span><br><span class="line">writeThread come in</span><br><span class="line">writeThread 写线程准备修改</span><br><span class="line">writeThread 写线程完成修改</span><br><span class="line">readThread 正在读取中.....<span class="number">.1</span>秒后 stampedLock.validate 方法值 <span class="literal">false</span></span><br><span class="line">readThread 正在读取中.....<span class="number">.2</span>秒后 stampedLock.validate 方法值 <span class="literal">false</span></span><br><span class="line">readThread 正在读取中.....<span class="number">.3</span>秒后 stampedLock.validate 方法值 <span class="literal">false</span></span><br><span class="line">写操作修改过 num 值</span><br><span class="line">从乐观读升级为悲观读</span><br><span class="line">悲观读后 num = <span class="number">3</span></span><br><span class="line">main <span class="number">3</span></span><br></pre></td></tr></table></figure>
</li>
<li><p>StampLock 缺点</p>
<ul>
<li>StampLock 不支持重入，没有 Re 开头</li>
<li>StampLock 的悲观读锁和写锁都不支持条件变量（Condition）</li>
<li>使用 StampLock 一定不要调用中断操作，即不要调用 <code>interrupt()</code> 方法（可能会产生难以解释的错误）</li>
</ul>
</li>
</ul>
</li>
</ol>
</article><div class="post-copyright"><div class="post-copyright__author"><span class="post-copyright-meta">文章作者: </span><span class="post-copyright-info"><a href="https://gitee.com/zcmmmm/zcmmmm">zcm</a></span></div><div class="post-copyright__type"><span class="post-copyright-meta">文章链接: </span><span class="post-copyright-info"><a href="https://gitee.com/zcmmmm/zcmmmm/2022/09/24/JUC%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B/">https://gitee.com/zcmmmm/zcmmmm/2022/09/24/JUC%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B/</a></span></div><div class="post-copyright__notice"><span class="post-copyright-meta">版权声明: </span><span class="post-copyright-info">本博客所有文章除特别声明外，均采用 <a href="https://creativecommons.org/licenses/by-nc-sa/4.0/" target="_blank">CC BY-NC-SA 4.0</a> 许可协议。转载请注明来自 <a href="https://gitee.com/zcmmmm/zcmmmm" target="_blank">无人深空</a>！</span></div></div><div class="tag_share"><div class="post-meta__tag-list"><a class="post-meta__tags" href="/tags/JUC/">JUC</a></div><div class="post_share"><div class="social-share" data-image="https://study-record-images.oss-cn-beijing.aliyuncs.com/Java/JUC/JUC.jpg" data-sites="facebook,twitter,wechat,weibo,qq"></div><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/butterfly-extsrc/sharejs/dist/css/share.min.css" media="print" onload="this.media='all'"><script src="https://cdn.jsdelivr.net/npm/butterfly-extsrc/sharejs/dist/js/social-share.min.js" defer></script></div></div><nav class="pagination-post" id="pagination"><div class="prev-post pull-left"><a href="/2022/09/28/JVM%20%E4%B8%8A%E7%AF%87-%E5%86%85%E5%AD%98%E4%B8%8E%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6/"><img class="prev-cover" src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Java/JVM/JVM.jpg" onerror="onerror=null;src='/img/404.jpg'" alt="cover of previous post"><div class="pagination-info"><div class="label">上一篇</div><div class="prev_info">JVM 上篇-内存与垃圾回收</div></div></a></div><div class="next-post pull-right"><a href="/2022/09/23/SpringBoot2%E5%85%A5%E9%97%A8-02/"><img class="next-cover" src="https://study-record-images.oss-cn-beijing.aliyuncs.com/Java/Spring/Spring.jpg" onerror="onerror=null;src='/img/404.jpg'" alt="cover of next post"><div class="pagination-info"><div class="label">下一篇</div><div class="next_info">SpringBoot2入门-02</div></div></a></div></nav></div><div class="aside-content" id="aside-content"><div class="sticky_layout"><div class="card-widget" id="card-toc"><div class="item-headline"><i class="fas fa-stream"></i><span>目录</span><span class="toc-percentage"></span></div><div class="toc-content is-expand"><ol class="toc"><li class="toc-item toc-level-1"><a class="toc-link" href="#JUC-%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B"><span class="toc-text">JUC 并发编程</span></a><ol class="toc-child"><li class="toc-item toc-level-2"><a class="toc-link" href="#1-Java-JUC-%E7%AE%80%E4%BB%8B"><span class="toc-text">1. Java JUC 简介</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#2-%E7%BA%BF%E7%A8%8B%E5%9F%BA%E7%A1%80%E7%9F%A5%E8%AF%86%E5%A4%8D%E4%B9%A0"><span class="toc-text">2. 线程基础知识复习</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#3-CompetableFuture"><span class="toc-text">3. CompetableFuture</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#4-Java-%E2%80%9D%E9%94%81%E2%80%9C-%E4%BA%8B"><span class="toc-text">4. Java ”锁“ 事</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#5-LockSupport-%E4%B8%8E%E7%BA%BF%E7%A8%8B%E4%B8%AD%E6%96%AD"><span class="toc-text">5. LockSupport 与线程中断</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#6-Java-%E5%86%85%E5%AD%98%E6%A8%A1%E5%9E%8B-JMM"><span class="toc-text">6. Java 内存模型 - JMM</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#7-volatile-%E4%B8%8E-JMM"><span class="toc-text">7. volatile 与 JMM</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#8-CAS%EF%BC%88compare-and-swap%EF%BC%89"><span class="toc-text">8. CAS（compare and swap）</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#9-%E5%8E%9F%E5%AD%90%E6%93%8D%E4%BD%9C%E7%B1%BB%E4%B9%8B-18-%E7%BD%97%E6%B1%89%E5%A2%9E%E5%BC%BA"><span class="toc-text">9. 原子操作类之 18 罗汉增强</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#10-%E8%81%8A%E8%81%8A-ThreadLocal"><span class="toc-text">10. 聊聊 ThreadLocal</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#11-Java-%E5%AF%B9%E8%B1%A1%E5%86%85%E5%AD%98%E5%B8%83%E5%B1%80%E5%92%8C%E5%AF%B9%E8%B1%A1%E5%A4%B4"><span class="toc-text">11. Java 对象内存布局和对象头</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#12-Synchronized-%E4%B8%8E%E9%94%81%E5%8D%87%E7%BA%A7"><span class="toc-text">12. Synchronized 与锁升级</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#13-AbstractQueuedSynchronizer-%E4%B9%8B-AQS"><span class="toc-text">13 AbstractQueuedSynchronizer 之 AQS</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#14-ReentreantLock%E3%80%81ReentrantReadWriteLock%E3%80%81StampedLock-%E8%AF%A6%E8%A7%A3"><span class="toc-text">14 ReentreantLock、ReentrantReadWriteLock、StampedLock 详解</span></a></li></ol></li></ol></div></div></div></div></main><footer id="footer"><div id="footer-wrap"><div class="copyright">&copy;2022 - 2023 By zcm</div><div class="framework-info"><span>框架 </span><a target="_blank" rel="noopener" href="https://hexo.io">Hexo</a><span class="footer-separator">|</span><span>主题 </span><a target="_blank" rel="noopener" href="https://github.com/jerryc127/hexo-theme-butterfly">Butterfly</a></div></div></footer></div><div id="rightside"><div id="rightside-config-hide"><button id="readmode" type="button" title="阅读模式"><i class="fas fa-book-open"></i></button><button id="darkmode" type="button" title="浅色和深色模式转换"><i class="fas fa-adjust"></i></button><button id="hide-aside-btn" type="button" title="单栏和双栏切换"><i class="fas fa-arrows-alt-h"></i></button></div><div id="rightside-config-show"><button id="rightside_config" type="button" title="设置"><i class="fas fa-cog fa-spin"></i></button><button class="close" id="mobile-toc-button" type="button" title="目录"><i class="fas fa-list-ul"></i></button><button id="go-up" type="button" title="回到顶部"><i class="fas fa-arrow-up"></i></button></div></div><div id="local-search"><div class="search-dialog"><nav class="search-nav"><span class="search-dialog-title">搜索</span><span id="loading-status"></span><button class="search-close-button"><i class="fas fa-times"></i></button></nav><div class="is-center" id="loading-database"><i class="fas fa-spinner fa-pulse"></i><span>  数据库加载中</span></div><div class="search-wrap"><div id="local-search-input"><div class="local-search-box"><input class="local-search-box--input" placeholder="搜索文章" type="text"/></div></div><hr/><div id="local-search-results"></div></div></div><div id="search-mask"></div></div><div><script src="/js/utils.js"></script><script src="/js/main.js"></script><script src="https://cdn.jsdelivr.net/npm/@fancyapps/ui/dist/fancybox.umd.min.js"></script><script src="https://cdn.jsdelivr.net/npm/node-snackbar/dist/snackbar.min.js"></script><script src="/js/search/local-search.js"></script><div class="js-pjax"></div><script src="/js/categories.js?v1"></script><script src="/js/navigation.js?v1"></script><script async data-pjax src="//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script></div></body></html>